Setiap pesan HTTP membawa lebih dari sekadar isi halaman: pesan itu juga membawa metadata berbentuk baris-baris pendek bernama header. Header inilah yang memberi tahu format body (Content-Type), tujuan pindah (Location), aturan caching (Cache-Control), hingga perintah menyimpan cookie (Set-Cookie). Tanpa header, browser tidak tahu harus merender HTML atau mengunduh berkas, dan klien API tidak tahu response sukses atau redirect. Pada chapter ini kita akan membedah header permintaan dan respons yang paling sering muncul, lalu mengirim serta membacanya langsung dari skrip PHP — termasuk aturan yang paling sering membuat pemula terjepit: header harus keluar sebelum output.
Apa Itu Header HTTP #
Header adalah baris berformat Nama: nilai yang menempel pada bagian awal setiap request dan response, setelah baris permintaan atau baris status. Satu pesan bisa membawa puluhan header, masing-masing satu baris, dan keduanya dipisahkan dari body oleh satu baris kosong. Nama header tidak peka kapitalisasi — Content-Type, content-type, dan CONTENT-TYPE adalah header yang sama — meski konvensi penulisan memakai huruf besar di tiap kata.
POST /api/produk HTTP/1.1
Host: api.tokobudi.example
Content-Type: application/json
Accept: application/json
{"nama": "Kopi Gayo", "harga": 45000}Pada pesan di atas, empat baris pertama setelah baris permintaan adalah header: server mengetahui domain tujuan dari Host, format body dari Content-Type, dan format jawaban yang diharapkan klien dari Accept. Baris kosong menutup bagian header, lalu body berupa JSON mengikuti. Anatomi respons sama persis, hanya baris status yang menggantikan baris permintaan.
Header Permintaan yang Sering Ditemui #
| Header | Isi | Kenapa penting |
|---|---|---|
Host | domain yang dituju | wajib pada HTTP/1.1; satu IP bisa melayani banyak domain |
User-Agent | identitas aplikasi klien | statistik dan kompatibilitas; tidak boleh dipercaya untuk keamanan |
Accept | tipe konten yang diinginkan klien | dasar content negotiation |
Content-Type | format body yang dikirim | menentukan cara server mem-parsing data |
Cookie | cookie milik domain tersebut | pembawa identitas sesi login |
Kelima header ini dikirim otomatis oleh browser dan curl, tetapi Anda juga bisa menambahkannya sendiri — curl -H "Accept: application/json" http://... — saat menguji API. Di sisi PHP, seluruh header permintaan tersedia dalam variabel $_SERVER dengan pola penamaan yang akan kita lihat pada bagian akhir chapter.
Header Respons yang Sering Dikirim #
| Header | Isi | Efeknya |
|---|---|---|
Content-Type | format body | browser merender HTML, menampilkan teks, atau mengunduh berkas |
Location | URL tujuan | pasangan wajib status 3xx; browser langsung pindah |
Cache-Control | aturan penyimpanan sementara | no-store melarang cache, max-age mengatur masa berlaku |
Set-Cookie | permintaan menyimpan cookie | browser menyimpan lalu mengirimkannya balik lewat Cookie |
Kombinasi Content-Type dan Location paling sering Anda sentuh sebagai penulis PHP. Content-Type: application/json wajib dikirim sebelum echo json_encode(...) agar klien tahu bahwa body adalah JSON. Location hampir selalu berpasangan dengan status 302 dan diikuti exit, karena kode setelah redirect biasanya tidak boleh lagi dieksekusi:
<?php
header("Location: /login");
exit;Tanpa exit, skrip tetap berjalan sampai selesai — response sudah diarahkan ke halaman login, tetapi efek samping seperti penghapusan data di baris berikutnya tetap terjadi sebelum eksekusi berhenti.
Mengirim Header dari PHP #
Fungsi header() menambahkan header ke response yang sedang disusun. Skrip berikut memasang dua header sebelum ada output apa pun:
<?php
header("Content-Type: application/json; charset=UTF-8");
header("X-Api-Version: 1");
var_dump(headers_sent());
var_dump(headers_list());Output yang Dihasilkan:
bool(false)
array(0) {
}Dua fungsi pembantu ikut terlihat di atas. headers_sent() melaporkan apakah header sudah lewat (bool(false) berarti masih boleh menambah) dan headers_list() seharusnya menampilkan daftar header yang terkumpul. Pada CLI hasilnya array(0) — SAPI CLI tidak pernah mengirim header ke mana pun sehingga daftarnya tidak dipelihara. Perilaku penuhnya baru terlihat di SAPI web. Skrip berikut dijalankan lewat server dan menampilkan daftarnya secara nyata:
<?php
// daftar-header.php — akses lewat: php -S 127.0.0.1:8099
// lalu buka http://127.0.0.1:8099/daftar-header.php
header("Content-Type: text/plain; charset=UTF-8");
header("X-Api-Version: 1");
echo "headers_sent: ";
var_export(headers_sent());
echo "\n";
print_r(headers_list());Hasil berikut dikirim server PHP sebagai body response ketika penulis Chapter mengaksesnya dari browser:
headers_sent: true
Array
(
[0] => X-Powered-By: PHP/8.4.23
[1] => Content-Type: text/plain; charset=UTF-8
[2] => X-Api-Version: 1
)Perhatikan dua hal dari hasil tersebut. Daftar header berisi dua header yang kita pasang sendiri ditambah X-Powered-By yang disisipkan otomatis oleh server PHP. Nilai headers_sent sudah true karena pemeriksaan dilakukan setelah echo pertama mengalir keluar — inilah jembatan menuju aturan paling penting chapter ini.
Saudara kandung header() yang perlu dikenal sejak awal adalah setcookie(). Fungsi ini sebenarnya pembungkus yang rapi: ia menyusun nilai dan atribut cookie, lalu menambahkan header Set-Cookie ke response:
<?php
$berhasil = setcookie("bahasa", "id", [
"expires" => 0,
"path" => "/",
"httponly" => true,
]);
var_dump($berhasil);Output yang Dihasilkan:
bool(true)Nilai bool(true) menandakan cookie berhasil disusun tanpa kesalahan. Opsi expires => 0 membuat cookie hidup selama sesi browser, path => "/" membuatnya berlaku untuk seluruh situs, dan httponly => true melarang JavaScript membacanya — satu lapisan pertahanan ekstra yang akan kita dalami pada Section keamanan. Seperti header(), setcookie() juga tunduk pada aturan sebelum output — dan aturan itulah yang sekarang kita bedah tuntas.
Aturan Header Sebelum Output #
Baris status dan header dikirim sebagai pembuka pesan, jadi PHP harus tahu keseluruhannya sebelum byte body pertama keluar. Begitu ada output — dari echo, print, var_dump, atau satu baris HTML sebelum tag <?php — PHP menandai “header sudah lewat” dan menolak semua pengaturan berikutnya. Fungsi headers_sent() memperlihatkan momen peralihan itu:
<?php
var_dump(headers_sent());
echo "satu baris output\n";
var_dump(headers_sent());Output yang Dihasilkan:
bool(false)
satu baris output
bool(true)Pemeriksaan pertama menghasilkan bool(false) karena belum ada satu byte pun yang keluar. Baris echo mengubah keadaan itu, dan pemeriksaan kedua menghasilkan bool(true) — sejak titik ini, header(), setcookie(), dan http_response_code() yang mengubah nilai akan ditolak. Apa yang terjadi bila aturan ini dilanggar? Berkas peringatan-header.php di bundle chapter ini sengaja melanggarnya:
<?php
echo "Output pertama keluar lebih dulu\n";
header("X-Uji: terlambat");
echo "Baris ini tetap tercetak\n";Jalankan dengan php peringatan-header.php dan PHP membalas dengan peringatan yang melegendaris:
Output pertama keluar lebih dulu
Warning: Cannot modify header information - headers already sent by (output started at peringatan-header.php:2) in peringatan-header.php on line 3
Baris ini tetap tercetakBaca pesannya dengan teliti — PHP menyebut dua lokasi berbeda. Bagian output started at ...:2 menunjuk baris tempat output pertama keluar (baris echo kedua berkas), sedangkan on line 3 menunjuk pemanggilan header() yang tertolak. Path lengkap berkas menyesuaikan lokasi di komputer Anda, dan skrip tetap selesai dengan exit code 0 karena ini hanya peringatan, bukan error fatal — di web, konsekuensinya lebih serius: header Anda benar-benar tidak terkirim.
Membaca Header Permintaan di PHP #
Untuk membaca header yang dikirim klien, PHP menyediakan dua jalur. Jalur pertama adalah fungsi apache_request_headers() beserta aliasnya getallheaders() — keduanya mengembalikan seluruh header permintaan sebagai array dengan nama aslinya. Namun kedua fungsi ini hanya terdefinisi ketika PHP berjalan sebagai modul Apache, lewat FastCGI/FPM, atau lewat built-in server; pada CLI keduanya tidak ada (pemeriksaan function_exists("getallheaders") menghasilkan false).
Jalur kedua berlaku universal: setiap header permintaan muncul di $_SERVER dengan nama yang diubah menjadi huruf besar, karakter - diganti _, dan diberi awalan HTTP_. Fungsi berikut membalik transformasi itu sehingga kita mendapatkan kembali nama header yang lazim:
<?php
function bacaHeaderPermintaan(array $server): array
{
$headers = [];
foreach ($server as $nama => $nilai) {
if (str_starts_with($nama, "HTTP_")) {
$namaHeader = strtolower(str_replace("_", "-", substr($nama, 5)));
$headers[$namaHeader] = $nilai;
}
}
ksort($headers);
return $headers;
}
$serverPalsu = [
"REQUEST_METHOD" => "GET",
"HTTP_HOST" => "www.tokobudi.example",
"HTTP_USER_AGENT" => "curl/8.14.1",
"HTTP_ACCEPT" => "application/json",
];
print_r(bacaHeaderPermintaan($serverPalsu));Output yang Dihasilkan:
Array
(
[accept] => application/json
[host] => www.tokobudi.example
[user-agent] => curl/8.14.1
)Mari kita telaah transformasinya:
- Baris 6 (
str_starts_with($nama, "HTTP_")): hanya kunci berawalanHTTP_yang merupakan header permintaan — kunci sepertiREQUEST_METHODdilewati karena menyimpan metadata lain. - Baris 7 (
substr($nama, 5)): membuang lima karakter awalanHTTP_, lalustr_replace("_", "-", ...)mengembalikan tanda hubung danstrtolower()menormalkan ke huruf kecil. - Hasil akhir:
HTTP_USER_AGENTmenjadiuser-agent,HTTP_ACCEPTmenjadiaccept— nama-nama yang persis seperti yang terlihat pada transkripcurl -vchapter pertama.
Dalam praktik framework modern, Anda jarang menulis fungsi semacam ini sendiri — Symfony HTTP Foundation, komponen Request di Laravel, dan PSR-7 sudah membungkusnya rapi. Meski begitu, mengetahui pola HTTP_* membuat Anda bisa membaca $_SERVER langsung saat debugging tanpa bergantung pada framework apa pun.
Ringkasan #
Header adalah metadata Nama: nilai yang menempel pada kedua arah pesan HTTP: klien mengirim Host, User-Agent, Accept, dan Cookie; server membalas dengan Content-Type, Location, Cache-Control, dan Set-Cookie. Dari PHP, header respons diatur lewat header() dan setcookie(), header permintaan dibaca lewat pola HTTP_* pada $_SERVER atau getallheaders() di SAPI web. Satu aturan mengikat semuanya: pengaturan header harus selesai sebelum output pertama keluar, atau PHP menolaknya dengan peringatan headers already sent. Header yang paling sering Anda atur sehari-hari adalah Content-Type — dan pendalaman format-format beserta PHP akan menjadi topik chapter berikutnya.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
contoh.php
Unduh berkas<?php
// contoh.php — rangkuman contoh Chapter "Headers"
// Jalankan dengan: php contoh.php
function bacaHeaderPermintaan(array $server): array
{
$headers = [];
foreach ($server as $nama => $nilai) {
if (str_starts_with($nama, "HTTP_")) {
$namaHeader = strtolower(str_replace("_", "-", substr($nama, 5)));
$headers[$namaHeader] = $nilai;
}
}
ksort($headers);
return $headers;
}
// Semua pengaturan header dilakukan sebelum output apa pun,
// sesuai aturan "header sebelum output".
header("Content-Type: application/json; charset=UTF-8");
header("X-Api-Version: 1");
$daftarHeader = headers_list();
$sentSebelumOutput = headers_sent();
echo "== headers_sent sebelum dan sesudah output ==\n";
var_dump($sentSebelumOutput);
echo "satu baris output\n";
var_dump(headers_sent());
echo "\n== Header respons yang terkumpul di CLI ==\n";
var_dump($daftarHeader);
echo "\n== Membaca header permintaan dari \$_SERVER ==\n";
$serverPalsu = [
"REQUEST_METHOD" => "GET",
"HTTP_HOST" => "www.tokobudi.example",
"HTTP_USER_AGENT" => "curl/8.14.1",
"HTTP_ACCEPT" => "application/json",
];
print_r(bacaHeaderPermintaan($serverPalsu));
peringatan-header.php
Unduh berkas<?php
// peringatan-header.php — demo aturan "header sebelum output"
// Jalankan dengan: php peringatan-header.php
// Peringatan yang muncul memang disengaja; exit code tetap 0.
echo "Output pertama keluar lebih dulu\n";
header("X-Uji: terlambat");
echo "Baris ini tetap tercetak\n";