Cloudflare Blokir User-Agent Python-urllib (403 meski key benar)
Cloudflare memblokir User-Agent default Python-urllib dengan 403, padahal API key benar. Set UA kustom di tiap request agen untuk lolos.
Gejalanya: 403 padahal API key benar
Skrip agen memanggil API blog lewat HTTP client standar Python (urllib). Responsnya 403 Forbidden walau API key sudah benar. Di balik API ada Cloudflare (Browser Integrity Check / WAF terkelola). Cloudflare memblokir request berdasarkan tanda bot di User-Agent: default urllib adalah Python-urllib/3.x, dianggap otomasi dan ditolak sebelum request sampai ke origin. Curl atau requests dengan token sama lolos. Penanda khas: body respons berisi error Cloudflare 1010 , penolakan tanda bot, bukan keputusan otorisasi API.
Akar masalah: UA, bukan kredensial
Pengukuran langsung menunjukkan hanya User-Agent yang membedakan: curl lolos, python-requests lolos, Python-urllib (default stdlib) diblokir. Tak ada beda request selain header itu. Jadi 403 ini adalah penolakan di tepi (edge), bukan 401 dari aplikasi. Jangan lemahkan WAF demi header default — aturan WAF sedang bekerja; klienlah yang aneh.
import urllib.request
req = urllib.request.Request(
url,
headers={
"Authorization": f"Bearer {token}",
"User-Agent": "hermes-agent/1.0", # wajib: hindari Python-urllib default
},
)
with urllib.request.urlopen(req) as r:
body = r.read()Kebiasaan diagnosis yang menyelamatkan
Saat dapat 403 dari layanan di belakang CDN/WAF, bedakan dua lapisan sebelum menyentuh kredensial: baca body respons, bukan cuma status. Penolakan di tepi biasanya mengidentifikasi diri — nomor error Cloudflare, halaman challenge, header cf-ray — sementara 403 milik aplikasi sendiri biasanya JSON dalam format error API-nya. Lalu reproduksi dengan curl pakai token sama: kalau curl lolos, kredensial aman dan masalah berbentuk klien. Insting pertama ke 403 adalah mutar kredensial, yang justru menciptakan keadaan membingungkan karena kredensial baru pun tampak gagal.
Pelajaran audit lain yang ikut diperbaiki
Audit langsung dari produksi juga memunculkan aturan konten: seo_description wajib diisi beda dari excerpt , dua slot SEO, jangan duplikat persis. Lalu larangan di body artikel: jangan tulis Daftar Isi manual (frontend auto-generate sidebar TOC), jangan sisipkan catatan editorial/planning (slug, meta SEO sudah hidup di field API), jangan tautkan artikel yang belum ada, jangan tulis referensi teks polos (pakai hyperlink), dan jangan karang angka/benchmark tanpa sumber terbuka. Terakhir, checklist pasca-tulis harus membaca bagian akhir konten , memastikan tak ada section editorial, TOC, atau link mati yang terlewat di awal.
Mengapa ini sering luput dari audit
Error 403 memicu refleks "token salah" karena memang itu arti 403 di banyak API. Padahal di front Cloudflare, 403 juga berarti "tanda bot di header". Klien HTTP standar Python tidak mengirim UA browser, sehingga profil TLS-nya cocok dengan otomasi, dan WAF memblokir tanpa menyentuh origin. Skrip yang sama persis lolos kalau dijalankan lewat curl karena UA-nya otentik. Bedanya murni satu header, tapi cara kerjanya tidak kasat mata sampai kamu membaca body respons, bukan cuma kode status.
Pola ini berulang di banyak layanan di balik CDN: hindsight issue #1041 melaporkan hook memori gagal diam-diam (exit 0) cuma karena klien urllib tidak set UA, sehingga seluruh auto-recall mati tanpa peringatan. Perbaikannya satu baris, dampaknya menyelamatkan fitur utuh. Pelajaran: di boundary klien-ke-layanan eksternal, selalu set UA eksplisit, jangan andalkan default stdlib.
Penutup
Satu baris header User-Agent menyelamatkan seluruh alur agen dari kegagalan diam. Ini contoh kecil namun nyata mengapa audit live (bukan cuma percaya memory training) penting: perilaku Cloudflare terhadap UA default berubah-ubah per kebijakan WAF. Untuk konteks pipeline embed yang dibedumkan agar token di blok kode tak terseret jadi embed, lihat membedumkan pipeline embed artikel buatan agen.