Alasan kami peduli: kami adalah Geonode dan kami menjual proxy, dan header respons adalah cara tercepat untuk menjawab pertanyaan yang paling sering diajukan pelanggan kepada kami — apakah ini masalah proxy atau bukan? Halaman pemblokiran, batasan kecepatan, dan kesalahan yang sesungguhnya terlihat identik di browser, namun sangat berbeda dalam header-nya. 429 dengan Retry-After berarti kecepatan Anda terlalu tinggi dan tidak ada proxy yang bisa memperbaikinya. 403 dengan header security-vendor berarti target telah mengidentifikasi Anda. Sebuah 407 berarti proxy meminta kredensial. Membaca header sebelum mengubah apa pun dapat menghemat banyak tebakan, dan curl memiliki opsi khusus untuk kasus ini — %{proxy_used}, yang ditambahkan pada versi 8.7.0, yang mengembalikan nilai 1 jika transfer dilakukan melalui proxy. Berguna ketika Anda tidak yakin apakah konfigurasi Anda telah berlaku.
Sekilas tentang Empat Opsi
| Opsi | Menampilkan | Mengirim | Cocok untuk |
|---|---|---|---|
-i | Header respons + isi | Permintaan Anda yang sebenarnya | Pemeriksaan sehari-hari |
-I | Hanya header respons | Permintaan HEAD | Pemeriksaan cepat, dengan catatan |
-D file | Header respons ke berkas | Permintaan Anda yang sebenarnya | Pembuatan skrip, pemisahan aliran |
-v | Header permintaan dan respons | Permintaan Anda yang sebenarnya | Debugging apa yang Anda kirim |
Baris yang paling penting adalah baris kedua, dan inilah sumber kebingungan terbesar dalam hal ini. Selebihnya hanya masalah ke mana outputnya diarahkan.
-i
: Header Bersama Isi
Opsi yang paling umum digunakan. Manual curl menjelaskannya sebagai -i, --show-headers
: "Menampilkan header respons dalam keluaran... Opsi ini membuat header respons disimpan dalam aliran/keluaran yang sama dengan datanya."
curl -i https://example.com
HTTP/2 200
content-type: text/html; charset=UTF-8
content-length: 1256
cache-control: max-age=604800
date: Wed, 02 Sep 2026 10:14:22 GMT
<!doctype html>...
Header, baris kosong, lalu isi — struktur yang sama dengan format wire.
Catatan penamaan yang mungkin membingungkan pembaca materi lama: bentuk panjangnya sekarang adalah --show-headers
. Dulu namanya --include
, dan keduanya berfungsi, tetapi dokumentasi saat ini menggunakan nama yang lebih baru.
Dua detail yang perlu diketahui. Saat output ditampilkan di terminal, curl mungkin menampilkan nama header dengan huruf tebal dan menandai URL Location:
, yang berguna saat interaktif tetapi tidak diinginkan dalam pipeline — perintah --no-styled-output` ` menonaktifkannya. Dan karena header dan isi berbagi aliran data, perintah -i dengan -o file` ` akan menulis keduanya ke dalam berkas, yang hampir selalu bukan yang Anda inginkan. Gunakan -D untuk kasus tersebut.
-I: Hanya Header, dan Mengapa Hal Ini Dapat Menyesatkan
-I dijelaskan sebagai berikut: "Hanya mengambil header. Server HTTP memiliki perintah HEAD yang digunakan untuk mengambil hanya header dari sebuah dokumen."
Baca dengan saksama. Perintah ini tidak mengambil respons dan mengabaikan isi dokumen. Metode HTTP yang dikirimkan berbeda.
curl -I https://example.com
Itu adalah permintaan HEAD, dan konsekuensinya nyata:
Beberapa server menangani HEAD secara berbeda. Sebuah HEAD mungkin mengembalikan header yang berbeda, kode status yang berbeda, atau ditolak sepenuhnya dengan 405 Method Not Allowed — sementara permintaan setara GET berfungsi dengan sempurna.
Beberapa kerangka kerja (framework) tidak menghitung isi (body) untuk permintaan HEAD, sehingga Content-Length, ETag, dan Content-Type mungkin tidak ada atau salah.
CDN dan cache sering kali memperlakukan HEAD sebagai kunci cache yang berbeda, sehingga header cache dapat berbeda dari yang akan dilihat oleh permintaan sebenarnya.
Sistem anti-bot mungkin merespons secara berbeda. Sebuah permintaan HEAD dari klien yang tidak biasa itu sendiri merupakan sinyal, dan tantangan yang Anda terima mungkin tidak sama dengan tantangan yang dihasilkan oleh permintaan GET.
Jadi, -I sangat baik untuk pemeriksaan cepat — apakah URL ini aktif, ke mana ia dialihkan, seberapa besar ukurannya — tetapi tidak dapat diandalkan untuk mendebug mengapa permintaan GET berperilaku aneh. Saat Anda mendiagnosis permintaan yang sebenarnya, gunakan metode yang sebenarnya:
curl -sS -o /dev/null -D - https://example.com
Perintah ini melakukan permintaan GET biasa, membuang isi permintaan ke /dev/null, dan menampilkan header ke output standar. Ini memberikan informasi yang seolah-olah diberikan oleh -I, tanpa mengubah permintaan tersebut.
Jika Anda memerlukan header dari permintaan POST secara khusus, pola yang sama berlaku:
curl -sS -o /dev/null -D - -X POST -H "Content-Type: application/json" \
-d '{"a":1}' https://api.example.com/items
-D dan -v: Memisahkan Aliran dan Melihat Permintaan
-D menulis header ke tujuan terpisah. Panduan pengguna: "Tulis header protokol yang diterima ke berkas yang ditentukan... Tentukan '-' sebagai nama berkas (tanda minus tunggal) agar ditulis ke stdout." Panduan tersebut juga menyebutkan bahwa jika tidak ada header yang diterima, opsi ini "akan membuat berkas kosong" — yang merupakan informasi diagnostik.
curl -D headers.txt -o body.html https://example.com
Pemisahan yang rapi, yang memang Anda inginkan dalam skrip. -D - mengirimkan header ke stdout sementara isi (body) dikirim ke mana pun yang ditunjuk oleh -o, dan kombinasi tersebut menjadi dasar pola di atas.
-v juga menampilkan permintaan, yang seringkali merupakan bagian yang sebenarnya Anda butuhkan. Manual menjelaskan awalan-awalan tersebut dengan tepat:
Baris keluaran terperinci diawali dengan huruf:
>header yang dikirim oleh curl,<header yang diterima oleh curl,}data yang dikirim oleh curl,{data yang diterima oleh curl,*informasi tambahan yang disediakan oleh curl.
curl -v https://example.com 2>&1 | grep '^>'
Hal ini memberikan Anda persis apa yang dikirimkan oleh curl — yang seringkali tidak sesuai dengan konfigurasi Anda, karena pustaka, pengaturan default, dan berkas .curlrc semuanya menambahkan dan mengganti header. Banyak sekali masalah "server mengabaikan header saya" dapat diselesaikan di sini.
Perhatikan bahwa keluaran terperinci diarahkan ke stderr, itulah sebabnya perintah 2>&1 diperlukan sebelum melakukan piping. Hal ini disengaja: agar isi (body) pada stdout tetap bersih.
Manual juga menyebutkan bahwa sejak curl 8.10, mengulangi perintah -v akan meningkatkan tingkat pelacakan (trace level). Untuk pekerjaan tingkat rendah yang sesungguhnya, --trace-ascii memberikan "dump pelacakan lengkap dari semua data masuk dan keluar, termasuk informasi deskriptif", dengan nilai heksadesimal dihilangkan agar tetap mudah dibaca.
Satu peringatan dari manual yang patut diulang: keluaran trace dan verbose "mungkin berisi data sensitif, termasuk nama pengguna, kredensial, atau konten data rahasia. Waspadalah dan berhati-hatilah saat membagikan log trace kepada orang lain." Kredensial proxy yang disertakan dalam URL akan muncul dalam keluaran verbose. Sunting bagian tersebut sebelum menempelkannya ke pelacak masalah.
Header yang Dapat Dibaca Mesin dengan %{header_json}
Opsi yang belum pernah dilihat kebanyakan orang, ditambahkan pada curl 7.83.0, dan merupakan solusi yang tepat setiap kali Anda hendak menulis ekspresi reguler untuk teks header.
Manual menjelaskannya sebagai "Sebuah objek JSON yang berisi semua header respons HTTP dari transfer terbaru. Nilai-nilai disajikan sebagai array, karena dalam kasus header ganda, dapat terdapat nilai-nilai yang berbeda." Nama-nama header ditampilkan "dalam huruf kecil, terdaftar sesuai urutan kemunculannya di jaringan", dengan duplikat "dikelompokkan berdasarkan kemunculan pertama header tersebut, dan setiap nilai disajikan dalam array JSON".
curl -s -o /dev/null -w '%{header_json}' https://example.com | jq
{
"content-type": ["text/html; charset=UTF-8"],
"cache-control": ["max-age=604800"],
"set-cookie": ["a=1; Path=/", "b=2; Path=/"]
}
Hal ini sekaligus memperbaiki tiga masalah. Nama-nama dinormalisasi menjadi huruf kecil, sehingga tidak ada pencocokan yang tidak peka huruf besar-kecil. Header yang berulang seperti Set-Cookie
ditampilkan sebagai array alih-alih digabungkan secara diam-diam. Dan hasilnya dapat diparsing tanpa perlu menulis parser.
Mengekstrak satu header menjadi sangat mudah:
curl -s -o /dev/null -w '%{header_json}' "$URL" | jq -r '.["retry-after"][0] // "none"'
Variabel-variabel -w
lain yang cocok dipadukan dengannya:
curl -s -o /dev/null -w 'status=%{response_code} redirects=%{num_redirects} proxy=%{proxy_used} ip=%{remote_ip}\n' "$URL"
response_code
adalah status transfer terakhir, num_redirects
menghitung jumlah pengalihan yang diikuti, redirect_url
menunjukkan ke mana pengalihan seharusnya mengarah jika Anda tidak menggunakan -L
, remote_ip
adalah alamat yang sebenarnya terhubung, dan proxy_used
mengembalikan nilai 1 jika ada proxy yang terlibat. Yang terakhir ini benar-benar berguna ketika pola NO_PROXY
mungkin secara diam-diam telah mengecualikan host Anda.
Mengikuti Rantai Pengalihan
Tanpa opsi ``-L`
, curl akan berhenti pada pengalihan pertama dan Anda hanya akan melihat respons tersebut. Dengan opsi ``-L
`, curl menampilkan header dari setiap respons dalam rantai:
curl -sSL -o /dev/null -D - https://example.com
HTTP/2 301
location: https://www.example.com/
HTTP/2 200
content-type: text/html
Setiap blok mewakili satu lompatan. Inilah cara Anda mengetahui bahwa sebuah URL melakukan pengalihan tiga kali, bahwa satu lompatan beralih ke HTTP biasa, atau bahwa pengalihan kehilangan cookie.
Dua pola yang patut diperhatikan:
curl -sSL -o /dev/null -w '%{num_redirects} hops -> %{url_effective}\n' "$URL"
Jumlah lompatan dan tujuan akhir dalam satu baris. Dan saat Anda ingin melihat ke mana pengalihan mengarah tanpa mengikutinya:
curl -s -o /dev/null -w '%{redirect_url}\n' "$URL"
Rantai pengalihan sebaiknya diperiksa lebih sering daripada yang biasanya dilakukan orang. Setiap lompatan adalah perjalanan pulang-pergi, rantai empat lompatan menambah latensi nyata, dan lompatan tak terduga melalui host yang berbeda seringkali menjadi penyebab masalah cookie atau CORS.
Header Melalui Proxy
Ada dua hal tambahan yang perlu diperhatikan, yang keduanya sering menimbulkan kebingungan pada awalnya.
** Respons CONNECT` ` muncul dalam output terperinci.** Untuk koneksi HTTPS melalui proxy HTTP, `curl` terlebih dahulu mengirimkan CONNECT untuk membuat terowongan, dan pertukaran data tersebut memiliki header tersendiri:
curl -v -x http://proxy.example.com:8080 https://example.com
Anda akan melihat CONNECT
, HTTP/1.1 200 Connection established
dari proxy, dan baru kemudian permintaan yang sebenarnya. Blok pertama tersebut merupakan komunikasi dari proxy, bukan dari target. Mengacaukan keduanya merupakan kesalahan umum yang sering terjadi pada awal-awal. Opsi ``--suppress-connect-headers akan menghapus blok tersebut dari output jika Anda hanya ingin melihat respons dari target.
%{http_connect}
melaporkan kode respons proxy khusus untuk permintaan CONNECT, terpisah dari status server tujuan. Perbedaan ini tepatnya yang Anda butuhkan saat terjadi kegagalan dan Anda tidak tahu hop mana yang menolak:
curl -s -o /dev/null -x "$PROXY" \
-w 'connect=%{http_connect} status=%{response_code} proxy=%{proxy_used}\n' \
https://example.com
connect=200 status=403
berarti proxy berfungsi dengan baik dan server tujuan yang menolak Anda. connect=407
berarti proxy meminta kredensial dan tidak pernah mencapai server tujuan. Kedua situasi tersebut memiliki solusi yang sama sekali berbeda, dan tanpa perbedaan ini, keduanya tampak identik dari sudut pandang aplikasi.
Perlu juga dicatat bahwa untuk HTTPS melalui terowongan, proxy tidak dapat menambahkan atau membaca header — proxy hanya meneruskan byte terenkripsi. Jika Anda melihat header yang tidak terduga pada respons HTTPS, header tersebut berasal dari target atau dari CDN di depannya, bukan dari proxy.
Apa yang Sebenarnya Diungkapkan oleh Header
Inti dari semua ini. Dengan membacanya dengan cermat, tebakan dapat berubah menjadi diagnosis.
Pertama, baris status. 200 berhasil. 301/302 dialihkan. 403 ditolak. 429 dibatasi laju. 407 memerlukan otentikasi proxy. 502/503 mengalami masalah pada server hulu.
Retry-After muncul bersama 429 dan 503 serta memberi tahu Anda secara tepat berapa lama harus menunggu. Mematuhinya adalah langkah yang benar sekaligus cara tercepat untuk kembali beroperasi. Mengabaikannya dan mencoba lagi segera adalah cara yang membuat batas sementara menjadi lebih lama.
Content-Type memberi tahu Anda apa yang sebenarnya Anda terima. text/html pada titik akhir API berarti Anda mendapatkan halaman kesalahan, bukan JSON, dan ini adalah jawaban atas sebagian besar kegagalan parsing.
Content-Length versus apa yang diterima. Isi yang pendek dengan panjang yang dinyatakan besar berarti terjadi pemotongan.
Header cache — Cache-Control, ETag, Last-Modified — memberi tahu Anda apakah Anda dapat menghindari pengunduhan ulang. If-None-Match dan If-Modified-Since pada permintaan berikutnya mengubah transfer penuh menjadi 304, yang pada koneksi dengan kuota bandwidth merupakan penghematan langsung.
Set-Cookie menunjukkan status sesi apa yang sedang dibuat oleh server, dan ketidakhadirannya di tempat yang Anda harapkan menjelaskan banyak masalah autentikasi.
Server dan header khusus vendor mengidentifikasi apa yang ada di depan server asal. Respons yang memuat header dari vendor keamanan dengan 403 memberi tahu Anda bahwa pemblokiran berasal dari lapisan perlindungan, bukan dari aplikasi — masalah yang berbeda dengan respons yang berbeda pula.
Header non-standar. Batas kuota, pengenal permintaan, dan metadata khusus API sering kali muncul sebagai header dengan awalan x-, dan hal-hal tersebut sering kali merupakan informasi paling berguna dalam respons. ID permintaan adalah informasi yang akan diminta oleh tim dukungan.
Pertanyaan Terkait
Bagaimana cara melihat header respons menggunakan curl?
curl -i URL menampilkan header diikuti oleh isi respons. curl -D - URL menampilkan header ke stdout secara terpisah. curl -v URL menampilkan baik header permintaan maupun header respons. Hindari menggunakan -I saat melakukan debugging permintaan nyata, karena perintah tersebut mengirimkan metode HEAD alih-alih GET.
Apa perbedaan antara -i dan -I di curl?
-i menyertakan header respons bersama dengan isi permintaan Anda yang sebenarnya. -I mengirimkan permintaan HEAD sebagai gantinya, sehingga ini adalah permintaan yang berbeda dengan hasil yang berpotensi berbeda. Gunakan -i atau -o /dev/null -D - saat Anda membutuhkan header dari permintaan yang sedang Anda debug.
Bagaimana cara melihat hanya header tanpa isi permintaan?
curl -sS -o /dev/null -D - URL. Perintah ini melakukan permintaan GET biasa, mengabaikan isi permintaan, dan menampilkan header. Ini memberikan hasil yang sama seperti yang ditampilkan oleh -I tanpa mengubah metode HTTP, yang penting karena beberapa server merespons permintaan HEAD secara berbeda atau bahkan menolaknya sama sekali.
Bagaimana cara melihat header permintaan yang dikirim oleh curl?
curl -v URL dan cari baris yang dimulai dengan >, yang merupakan header yang dikirim oleh curl. Keluaran terperinci (verbose) diarahkan ke stderr, jadi alirkan ke 2>&1 jika Anda ingin menyaringnya. Inilah cara memastikan bahwa header yang Anda konfigurasikan benar-benar terkirim.
Bagaimana cara mendapatkan header curl dalam format JSON?
curl -s -o /dev/null -w '%{header_json}' URL. Ditambahkan pada curl 7.83.0, fitur ini menampilkan semua header respons sebagai objek JSON dengan nama huruf kecil dan nilai array, sehingga header yang berulang seperti Set-Cookie tetap dipertahankan alih-alih digabungkan. Salurkan ke jq untuk mengekstrak bidang-bidangnya.
Mengapa saya melihat dua set header saat menggunakan proxy?
Untuk koneksi HTTPS melalui proxy HTTP, curl terlebih dahulu mengirimkan CONNECT untuk membuka terowongan, dan respons proxy terhadap permintaan tersebut muncul sebelum respons target. Gunakan --suppress-connect-headers untuk menyembunyikannya, atau %{http_connect} untuk membaca kode status proxy secara terpisah dari kode status target.
Bagaimana cara melihat header untuk setiap pengalihan?
Tambahkan -L agar curl mengikuti pengalihan, dan gunakan -D - atau -i — curl akan menampilkan header dari setiap respons dalam rantai, satu blok per lompatan. %{num_redirects} dan %{url_effective} memberikan jumlah dan URL akhir dalam satu baris.
Apakah header respons menunjukkan apakah saya diblokir?
Seringkali, ya, dan lebih dapat diandalkan daripada isi respons. Sebuah 429 dengan Retry-After menandakan pembatasan laju (rate limit). Sebuah 403 yang membawa header dari vendor keamanan merupakan lapisan perlindungan. Sebuah 200 dengan Content-Type: text/html pada titik akhir API menandakan halaman tantangan atau halaman login. Masing-masing mengindikasikan solusi yang berbeda, dan hanya header-lah yang dapat membedakannya.
Kesimpulan
Empat opsi dan satu jebakan umum. -i untuk pemeriksaan sehari-hari, -D - saat Anda ingin header terpisah dari isi pesan, -v saat Anda perlu melihat apa yang Anda kirim serta respons yang diterima, dan -I hanya untuk pemeriksaan ketersediaan cepat — karena perintah ini mengirimkan permintaan HEAD, dan server berhak merespons permintaan HEAD secara berbeda dari permintaan GET.
Pilihan yang layak diadopsi jika Anda hanya mengambil satu hal dari ini adalah %{header_json}. Setiap skrip yang saat ini mengurai teks header dengan ekspresi reguler sebaiknya menggunakan ini sebagai gantinya: nama huruf kecil, array untuk header yang berulang, dan output yang dapat dibaca oleh jq. Dipadukan dengan %{response_code}, %{num_redirects}, dan %{proxy_used}, hal ini mengubah pemeriksaan header menjadi sesuatu yang dapat Anda verifikasi secara otomatis daripada hanya memeriksanya secara manual.
Dan ketika sebuah permintaan mengalami kesalahan, bacalah header-nya sebelum mengubah apa pun. Kode status, Retry-After, Content-Type, dan header vendor apa pun di antaranya biasanya secara langsung menyebutkan masalahnya — yang jauh lebih baik daripada mencoba-coba pengaturan hingga sesuatu berhasil, dan hanya membutuhkan waktu sekitar sepuluh detik.
