Kode status HTTP: panduan lengkap 1xx-5xx untuk developer
Anda membuka DevTools dan tab Network setengahnya merah. Endpoint Anda menghasilkan 502 di production, 200 di lokal, dan rekan kerja di Slack baru saja bertanya, “Ini seharusnya 401 atau 403?”. Kode status HTTP terlihat sederhana, tiga digit dalam lima kelompok, tapi pilihan yang salah membocorkan informasi, merusak SEO, dan membuat giliran on-call jadi mimpi buruk.
Panduan ini adalah lembar contekan (cheat sheet) lengkap kode status HTTP untuk developer yang bekerja sehari-hari. Anda mendapatkan tiga hal: (1) tabel referensi cepat untuk setiap kode yang benar-benar Anda temui di lapangan, (2) matriks keputusan untuk pasangan yang sering tertukar (301 vs 302, 401 vs 403, 404 vs 410, 502 vs 504), dan (3) bagian tooling yang menunjukkan cara memeriksa kode status dari curl, fetch, dan Python requests. Setiap kode di bawah berlandaskan pada RFC 9110, standar semantik HTTP saat ini, serta Registri Kode Status HTTP IANA.
Referensi cepat: semua kode status HTTP sekilas
Berikut kode-kode yang akan Anda temui di production, dikelompokkan per kelas. Bookmark tabel ini, sisa artikel akan menjelaskan kode-kode yang lebih rumit.
| Kode | Nama | Kapan Anda akan melihatnya |
|---|---|---|
| 100 | Continue | Mengirim body POST besar dengan Expect: 100-continue |
| 101 | Switching Protocols | Handshake WebSocket, upgrade HTTP/2 |
| 103 | Early Hints | Server mendorong header Link sebelum respons utama |
| 200 | OK | Sukses default untuk GET, PUT, PATCH |
| 201 | Created | POST yang membuat resource (mengembalikan Location) |
| 202 | Accepted | Job asinkron diantrekan, pekerjaan belum selesai |
| 204 | No Content | DELETE sukses, PUT tanpa body untuk dikembalikan |
| 206 | Partial Content | Range request, video seek, download yang bisa di-resume |
| 301 | Moved Permanently | URL lama dipensiunkan, mesin pencari mentransfer link equity |
| 302 | Found | Redirect sementara, URL asli tetap kanonikal |
| 303 | See Other | Pola Post/Redirect/Get setelah POST formulir |
| 304 | Not Modified | GET kondisional dengan ETag atau If-Modified-Since cocok |
| 307 | Temporary Redirect | Seperti 302, tapi method dan body dipertahankan |
| 308 | Permanent Redirect | Seperti 301, tapi method dan body dipertahankan |
| 400 | Bad Request | JSON rusak, field wajib hilang, schema gagal |
| 401 | Unauthorized | Tanpa kredensial atau token kedaluwarsa |
| 403 | Forbidden | Sudah terautentikasi tapi tidak diizinkan |
| 404 | Not Found | Resource tidak ada (atau Anda menyembunyikannya) |
| 405 | Method Not Allowed | POST ke endpoint yang hanya menerima GET (wajib menyertakan Allow) |
| 408 | Request Timeout | Klien terlalu lama mengirim permintaan |
| 409 | Conflict | Optimistic-lock gagal, kunci ganda |
| 410 | Gone | Resource dihapus permanen, tidak akan kembali |
| 415 | Unsupported Media Type | Content-Type salah, mis. XML ke API JSON |
| 422 | Unprocessable Content | Sintaks valid, semantik invalid (validation error) |
| 425 | Too Early | Risiko replay early-data TLS 1.3 |
| 428 | Precondition Required | Server mewajibkan If-Match untuk mencegah lost update |
| 429 | Too Many Requests | Terkena rate limit (wajib menyertakan Retry-After) |
| 451 | Unavailable for Legal Reasons | DMCA, takedown GDPR, geo-block |
| 500 | Internal Server Error | Eksepsi tak tertangani di kode Anda |
| 501 | Not Implemented | Method atau fitur tidak didukung (jarang di REST) |
| 502 | Bad Gateway | Upstream mengembalikan respons tidak valid |
| 503 | Service Unavailable | Mode maintenance atau kelebihan beban |
| 504 | Gateway Timeout | Upstream tidak merespons tepat waktu |
| 507 | Insufficient Storage | WebDAV kehabisan disk |
| 508 | Loop Detected | Redirect tak terbatas atau rekursi di WebDAV |
| 511 | Network Authentication Required | Captive portal di WiFi hotel/bandara |
Sisa artikel ini menjabarkan setiap kelas dengan matriks keputusan, anti-pattern, dan konsekuensi SEO ketika Anda salah memilih.
Cara kerja kode status HTTP (anatomi 3 digit)
Mengapa tiga digit?
Kode status HTTP terdiri dari tiga digit desimal karena HTTP/0.9 membutuhkan sinyal berukuran tetap yang cukup kecil agar parser dapat bercabang dengan cepat dan cukup besar untuk menyisakan ruang bagi kode baru. Tiga digit memberi Anda 900 kemungkinan nilai (100–999), lebih dari cukup. Registri IANA hari ini hanya memakai sekitar 60 di antaranya.
Digit pertama menunjukkan kelas. Digit kedua dan ketiga adalah kode spesifik di dalam kelas tersebut. Klien yang tidak mengenali 418 harus jatuh kembali (fallback) ke penanganan generik 4xx. RFC 9110 §15 menyatakan ini secara eksplisit: klien harus memperlakukan kode yang tidak dikenal sebagai x00 dari kelasnya.
Lima kategori sekilas
| Kelas | Arti | Body wajib? | Cacheable secara default? |
|---|---|---|---|
1xx | Informational, sementara, masih ada lanjutan | Tidak | Tidak |
2xx | Success, permintaan dipahami dan diterima | Sering | Tergantung method |
3xx | Redirection, perlu tindakan lanjutan | Opsional | 301, 308 ya; 302, 307 tidak |
4xx | Client error, kesalahan Anda, perbaiki permintaan | Ya (jelaskan) | Umumnya tidak |
5xx | Server error, kesalahan kami, retry mungkin membantu | Ya (jelaskan) | Tidak |
Kolom “cacheable secara default” itu penting. CDN dan browser meng-cache 301 dan 308 dengan agresif dan selamanya. Memilih kode redirect yang salah di production sulit dibatalkan karena pengguna sudah memiliki redirect tersimpan secara lokal. Kita akan kembali ke poin ini di bagian SEO.
Jika Anda ingin menggali lebih dalam tentang struktur URL (yang menjadi objek operasi kode redirect), URL Encoding & Decoding menjelaskan percent-encoding, query string, dan jalur byte-level yang menentukan apa yang membuat sebuah URL valid sejak awal.
1xx — informational (kapan Anda benar-benar akan melihatnya)
Sebagian besar developer bertahun-tahun tidak pernah melihat 1xx secara langsung. Kode-kode ini adalah respons sementara: server memberi tahu klien, “Saya masih di sini, lanjutkan.” Browser DevTools biasanya menyembunyikannya, dan kebanyakan library HTTP menggabungkannya ke respons akhir.
Untuk setiap kode di bawah, referensi status respons HTTP MDN adalah rujukan silang yang paling ramah jika Anda ingin pendapat kedua tentang sebuah definisi.
100 Continue
Klien mengirim Expect: 100-continue di header dan menunggu sebelum mengirim body permintaan yang besar. Server membalas 100 Continue jika bersedia menerima body tersebut, atau 4xx jika permintaan akan ditolak. Cara ini menghemat bandwidth pada upload besar; percuma mengirim 200 MB jika server akan menolaknya karena header yang hilang.
curl -v -H "Expect: 100-continue" \
-H "Content-Type: application/octet-stream" \
--data-binary @big-file.bin \
https://api.example.com/upload
Jika Anda tidak melihat < HTTP/1.1 100 Continue di output verbose, kemungkinan klien Anda menghapus header tersebut atau server tidak mendukungnya.
101 Switching Protocols
Handshake yang mengubah koneksi HTTP menjadi koneksi WebSocket atau HTTP/2. Klien mengirim Upgrade: websocket, server membalas 101 Switching Protocols, dan sejak titik itu koneksi berbicara dalam protokol berbeda. Anda akan melihat ini di tab Network pada aplikasi chat, dashboard live, atau alat kolaborasi mana pun.
103 Early Hints
Kode yang relatif baru (RFC 8297, 2017) yang memungkinkan server mengirim header Link untuk hint preload sebelum respons utama siap. Browser mulai mengambil CSS dan JS sementara server masih merender. Per 2026, Cloudflare, Fastly, dan Vercel semuanya mendukung 103 di production, dan ini adalah alternatif modern untuk HTTP/2 server push (yang sudah deprecated di Chrome).
HTTP/1.1 103 Early Hints
Link: </styles.css>; rel=preload; as=style
Link: </app.js>; rel=preload; as=script
HTTP/1.1 200 OK
Content-Type: text/html
...
Cek anti-pattern. Jika klien Anda tidak pernah menerima kode 1xx saat Anda mengharapkannya, pelakunya biasanya adalah reverse proxy. Versi nginx yang lebih lama menghapus Expect: 100-continue dan 103 Early Hints. Periksa konfigurasi proxy sebelum berasumsi server bermasalah.
2xx — success (lebih dari sekadar 200)
Mengembalikan 200 OK untuk semua hal adalah code-smell paling umum di REST API. Keluarga 2xx membawa informasi semantik yang membuat klien lebih cerdas dan cache lebih efisien.
200 OK
Pilihan default. GET mengembalikan resource, PUT mengembalikan resource yang sudah diperbarui (atau 204), PATCH mengembalikan resource yang sudah dipatch. Jika tidak ada alasan untuk memakai kode yang lebih spesifik, gunakan 200.
201 Created
POST yang membuat resource baru harus mengembalikan 201 plus header Location yang menunjuk ke resource baru tersebut. Begitulah cara klien RESTful menemukan URL kanonikal dari benda yang baru saja mereka buat.
HTTP/1.1 201 Created
Location: /api/users/42
Content-Type: application/json
{"id": 42, "name": "Ada Lovelace"}
202 Accepted
Server menerima permintaan tapi belum selesai memprosesnya. Gunakan ini untuk pekerjaan asinkron, dan klien sebaiknya melakukan polling, berlangganan webhook, atau memeriksa endpoint status. Sertakan ID job di body.
204 No Content
Sukses, tanpa body. Umum untuk DELETE (resource sudah hilang, apa lagi yang mau dikembalikan?) dan untuk operasi PUT di mana klien sudah tahu state baru. Browser tidak akan mengubah halaman saat ini jika submit formulir mengembalikan 204, dan ini berguna untuk aksi fire-and-forget di single-page app.
206 Partial Content
Dikembalikan untuk range request: klien meminta byte 1000-2000 dengan header Range: bytes=1000-2000, dan server merespons hanya dengan potongan tersebut. Streaming video, download yang dapat di-resume, dan sinkronisasi file berbasis HTTP semuanya bergantung pada 206.
Keputusan: 200 vs 201 vs 204 untuk POST
| Skenario | Kode | Body |
|---|---|---|
| POST membuat resource baru | 201 Created | Resource baru (atau hanya ID) + Location |
| POST memicu pekerjaan asinkron, hasil belum siap | 202 Accepted | ID job, URL polling |
POST adalah aksi tanpa resource (mis. /login) | 200 OK | Hasil aksi (token, status) |
| POST sukses tapi respons kosong | 204 No Content | (tidak ada) |
Jika Anda tidak bisa memutuskan antara 200 dan 201, tanyakan: “apakah server membuat sebuah resource yang sekarang punya URL sendiri?” Jika ya, 201. Jika tidak, 200.
3xx — redirection (301 vs 302 vs 307 vs 308)
Redirect adalah kelas yang paling sering disalahgunakan. Perbedaan antara 301, 302, 307, dan 308 bermuara pada tiga pertanyaan ortogonal: apakah perpindahan permanen, apakah method dipertahankan, dan apakah respons cacheable.
301 Moved Permanently
Resource sudah pindah dan tidak akan kembali. Mesin pencari mentransfer link equity ke URL baru. Browser dan CDN meng-cache 301 tanpa batas waktu. Jika Anda mengarahkan ulang /old ke /new dengan 301 lalu berubah pikiran, pengguna dengan redirect yang ter-cache akan terus pergi ke /new selamanya (atau sampai mereka menghapus cache).
Secara historis, browser bisa menulis ulang method permintaan pada 301 (POST → GET), itulah sebabnya HTTP/1.1 memperkenalkan 308 untuk memperbaiki hal tersebut.
302 Found
Redirect sementara. URL asli tetap kanonikal, dan mesin pencari sebaiknya terus mengindeks URL asli. Gunakan ini untuk routing A/B testing, halaman maintenance, atau alur “login untuk melanjutkan”.
Seperti 301, browser secara historis menulis ulang POST menjadi GET pada 302. Jika Anda perlu mengarahkan ulang POST dan mempertahankannya sebagai POST, gunakan 307 saja.
303 See Other
Selalu menulis ulang method menjadi GET. Pola Post/Redirect/Get: formulir dikirim ke /submit, server mengembalikan 303 dengan Location: /thank-you, browser melakukan GET /thank-you. Refresh halaman thank-you tidak mengirim ulang formulir. Untuk inilah 303 dirancang.
304 Not Modified
Respons kondisional. Klien mengirim If-None-Match: "abc123" (atau If-Modified-Since), server memeriksa apakah resource berubah, dan jika tidak, mengembalikan 304 tanpa body. Browser memakai salinan cache-nya. Begitulah setiap CDN dan lapisan caching menjaga situs Anda tetap cepat.
307 Temporary Redirect
Seperti 302, tapi method tidak boleh berubah. POST tetap POST, body dipertahankan. Gunakan ini ketika Anda ingin redirect sementara pada permintaan non-GET.
308 Permanent Redirect
Seperti 301, tapi method tidak boleh berubah. Pilihan modern dan lebih aman untuk redirect permanen pada API yang menerima POST/PUT.
Matriks keputusan: kode redirect mana?
| Permanen (cache selamanya) | Sementara (jangan di-cache) | |
|---|---|---|
| Method boleh berubah jadi GET | 301 Moved Permanently | 302 Found |
| Method harus tetap sama | 308 Permanent Redirect | 307 Temporary Redirect |
Kasus khusus: jika Anda secara spesifik menginginkan POST → GET (pola Post/Redirect/Get), gunakan 303 See Other.
Untuk halaman HTML dengan navigasi browser, 301 dan 302 biasanya cukup karena GET tetap GET. Untuk API dan formulir, prioritaskan 308 dan 307 agar terhindar dari penulisan ulang method yang mengejutkan.
4xx — client errors (memilih yang tepat)
4xx berarti klien melakukan sesuatu yang salah. Semakin kaya kosakata 4xx Anda, semakin mudah API Anda dipakai, karena klien dapat bercabang berdasarkan kode alih-alih mem-parse string error.
400 Bad Request
Error sintaks generik. JSON yang rusak, field wajib yang hilang di tingkat struktural, permintaan yang bahkan tidak bisa di-parse server. Jika permintaan ter-parse tapi gagal validasi bisnis, lebih baik 422.
401 Unauthorized vs 403 Forbidden
Pasangan paling membingungkan di HTTP. Pembedanya sederhana begitu Anda melihatnya:
401 Unauthorizedberarti permintaan kekurangan autentikasi yang valid. Server tidak tahu siapa Anda. Mengirim ulang kredensial (atau menyegarkan token) mungkin memperbaikinya. Respons harus menyertakan headerWWW-Authenticateper RFC 9110 §15.5.2.403 Forbiddenberarti server tahu siapa Anda dan tetap menolak. Mengirim ulang permintaan tidak akan membantu. Anda butuh kredensial berbeda atau izin berbeda.
| Anda melihat | Yang sebenarnya terjadi |
|---|---|
401 dengan WWW-Authenticate: Bearer | Tidak ada token, token kedaluwarsa, atau token invalid |
403 setelah login berhasil | Sudah login, tapi user ini tidak boleh mengakses resource ini |
401 setelah login berhasil | Bug, kemungkinan besar Anda butuh 403 |
Anti-pattern: 403-as-404. Beberapa situs mengembalikan 403 ketika user tanpa autentikasi meminta /admin/dashboard. Cara ini membocorkan keberadaan /admin/dashboard. GitHub menyelesaikannya dengan mengembalikan 404 untuk repository privat yang Anda bukan anggotanya, sehingga resource “tidak ada” dari sudut pandang Anda. Itu pilihan penyembunyian informasi yang disengaja, bukan bug.
404 Not Found vs 410 Gone
Keduanya berkata “resource ini tidak ada di sini.” Bedanya pada permanensi dan SEO.
404 Not Foundberarti mungkin ada, mungkin tidak, mungkin akan kembali. Mesin pencari akan terus memeriksanya.410 Goneberarti pernah ada, dihapus dengan sengaja, tidak akan kembali. Mesin pencari mengeluarkannya dari indeks jauh lebih cepat.
Jika Anda menghapus halaman produk dan ingin keluar dari indeks Google sekarang, 410 adalah pilihan tepat. Jika sebuah URL hanya rusak sementara, 404 sudah cukup.
405 Method Not Allowed
URL ada tapi tidak menerima method ini. Respons harus menyertakan header Allow yang mendaftarkan method yang didukung.
HTTP/1.1 405 Method Not Allowed
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
{"error": "POST is not allowed on this endpoint"}
Lupa header Allow adalah pelanggaran kontrak nomor 1 di REST API yang dibangun manual.
408 Request Timeout
Klien mulai mengirim permintaan lalu diam. Server menyerah. Berbeda dari 504 Gateway Timeout, yang berkaitan dengan upstream; 408 adalah “Anda, klien, terlalu lama.”
409 Conflict
Permintaan bertentangan dengan state saat ini. Penggunaan paling umum: optimistic locking. Klien mengirim If-Match: "etag-v3" dan ETag server saat ini adalah "etag-v4", jadi pembaruan ditolak dengan 409.
410 Gone
Lihat di atas: penghapusan permanen. Berguna untuk mengeluarkan record yang dihapus secara soft dari indeks pencarian.
415 Unsupported Media Type
Klien mengirim body yang tidak dipahami server. POST XML ke API yang hanya menerima JSON akan menghasilkan 415. Respons sebaiknya memberi petunjuk tipe yang dapat diterima.
422 Unprocessable Content
Permintaan ter-parse dengan baik, tapi gagal validasi semantik. RFC 9110 akhirnya mempromosikan kode ini dari WebDAV ke spesifikasi inti pada 2022. Gunakan 422 untuk error validasi:
{
"error": "validation_failed",
"details": [
{"field": "email", "message": "must be a valid email"},
{"field": "age", "message": "must be at least 13"}
]
}
Jika API Anda tidak bisa memutuskan antara 400 dan 422, aturan praktisnya: 400 untuk “saya bahkan tidak bisa mem-parse ini,” 422 untuk “saya sudah parse dan isinya tidak masuk akal.”
425 Too Early
Dikirim saat server tidak ingin mengambil risiko memproses permintaan yang mungkin merupakan replay early-data TLS 1.3. Sebagian besar relevan untuk CDN dan reverse proxy.
428 Precondition Required
Server bersikeras Anda mengirim If-Match atau If-Unmodified-Since untuk menghindari masalah lost-update. Dipakai pada API editing kolaboratif.
429 Too Many Requests
Terkena rate limit. Respons harus menyertakan Retry-After (dalam detik, atau sebagai HTTP date) agar klien yang sopan dapat mundur sejenak.
HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/json
{"error": "rate_limited", "limit": 100, "window": "1m"}
451 Unavailable for Legal Reasons
Angkanya adalah referensi ke karya Bradbury (Fahrenheit 451). Kasus penggunaannya bukan fiksi: takedown DMCA, penghapusan right-to-be-forgotten GDPR, dan geo-block tingkat negara semuanya membenarkan 451. Respons sebaiknya menyertakan header Link yang menunjuk ke otoritas hukum yang mewajibkan blokir, sesuai RFC 7725.
418 I’m a Teapot (easter egg)
Ya, ini nyata. RFC 2324 (April Mop 1998) dan IETF mempertahankannya karena terlalu banyak produk yang ikut-ikutan mengimplementasikannya sebagai lelucon. Jangan rilis 418 di API sungguhan, karena kebanyakan reverse proxy dan load balancer akan salah menanganinya.
Matriks keputusan: 4xx mana?
| Situasi | Kode |
|---|---|
| Body rusak atau tidak bisa di-parse | 400 |
| Tanpa autentikasi / token kedaluwarsa | 401 |
| Sudah terautentikasi tapi tidak diizinkan | 403 |
| URL tidak ada (atau Anda menyembunyikannya) | 404 |
| URL pernah ada, dihapus dengan sengaja | 410 |
| Method HTTP salah | 405 (dengan Allow) |
Content-Type salah | 415 |
| Konflik optimistic-lock | 409 |
| Error validasi (parse oke, validasi gagal) | 422 |
| Terkena rate limit | 429 (dengan Retry-After) |
| Diblokir karena alasan hukum | 451 |
5xx — server errors (apa yang sebenarnya rusak)
5xx adalah “kesalahan kami.” Engineer yang on-call paling peduli pada 5xx mana yang membangunkan mereka pukul 3 pagi, karena kodenya memberi tahu lapisan mana yang harus diselidiki lebih dulu.
500 Internal Server Error
Penampung serbaguna. Hampir selalu berarti eksepsi tak tertangani naik ke handler default framework. Kode ini tidak memberi tahu apa-apa tentang penyebabnya, dan itulah sebabnya structured logging lebih penting daripada kode statusnya di sini.
501 Not Implemented
Server sama sekali tidak mendukung method tersebut. Berbeda dari 405 (method ini tidak diizinkan untuk URL ini); 501 berkata “server ini tidak tahu apa itu PROPFIND.” Jarang di REST API.
502 Bad Gateway
Sebuah reverse proxy atau load balancer menerima respons tidak valid dari upstream. Upstream membalas, tapi dengan sampah: protokol salah, header rusak, koneksi terputus di tengah respons. Jika Anda melihat 502 dari CDN, kemungkinan origin sedang crash atau mengembalikan body yang terpotong.
503 Service Unavailable
Server sengaja tidak melayani permintaan saat ini. Gunakan ini untuk jendela maintenance atau respons graceful overload. Sebaiknya menyertakan Retry-After.
504 Gateway Timeout
Reverse proxy menunggu upstream dan upstream tidak pernah membalas tepat waktu. Upstream lambat atau macet, berbeda dari 502, di mana upstream membalas dengan sampah.
502 vs 504: diagnosis on-call
| Anda melihat | Hal pertama yang perlu diperiksa |
|---|---|
502 Bad Gateway | Upstream membalas dengan data tidak valid; periksa log origin untuk crash, respons rusak, ketidakcocokan protokol |
504 Gateway Timeout | Upstream menggantung; periksa CPU origin, query DB, panggilan API downstream, dan proxy_read_timeout proxy |
Kebingungan umum: query database yang memakan waktu 60 detik akan muncul sebagai 504 jika proxy Anda time out di 30 detik, tapi sebagai 500 jika app server time out di 90 detik dan melempar eksepsi. Akar masalah sama, kode beda, baris log beda; latih dashboard Anda untuk menampilkan keduanya.
507 Insufficient Storage
Spesifik WebDAV. Disk di server penuh. Jika Anda melihat ini dari API non-WebDAV, ada yang memaksakan makna kode tersebut.
508 Loop Detected
Rekursi tak terbatas pada operasi PROPFIND WebDAV. Sangat jarang.
511 Network Authentication Required
Kode captive portal: WiFi di hotel atau bandara mengirim 511 untuk memberi tahu browser Anda “Anda perlu login ke portal dulu.” Respons mencakup Location ke halaman portal.
Matriks troubleshooting: lapisan mana yang diperiksa lebih dulu
| Kode | App | Proxy | DB | Network |
|---|---|---|---|---|
500 | Ya | — | Mungkin (error DB tak tertangani) | — |
502 | — | Ya (upstream rusak) | — | Mungkin (TCP reset) |
503 | Ya (flag maintenance) | Ya (rate-limit reject) | — | — |
504 | Ya (handler lambat) | Ya (konfig timeout) | Ya (query lambat) | Ya (DNS, packet loss) |
Anti-pattern kode status HTTP yang umum
Lima kesalahan ini menyumbang sebagian besar kode buruk yang saya tinjau.
1. Membungkus error di dalam 200 OK
HTTP/1.1 200 OK
{"success": false, "error": "user_not_found"}
Setiap alat monitoring, CDN, dan cache sekarang mengira permintaan sukses. Logika retry gagal. Load balancer yang status-code-aware merutekan trafik buruk ke backend “sehat”. Pola ini berasal dari JSON-RPC dan diwarisi GraphQL. GraphQL melakukannya karena partial success butuh pelaporan error per field, dan itu wajar. REST tidak punya alasan: pakai 4xx untuk client error, 5xx untuk server error, dan letakkan detail terstruktur di body.
2. Mencampur 401 dan 403
Jika 401 dan 403 Anda tidak konsisten, penyerang dapat menyelidiki API Anda untuk menemukan resource mana yang ada. Pilih satu kebijakan: kembalikan 404 untuk “Anda tidak boleh melihat ini” (pendekatan GitHub untuk repo privat), atau kembalikan 403 secara konsisten. Inkonsistensi membocorkan informasi.
3. Menyembunyikan 403 di balik 404
Kadang benar, sering jadi bug. GitHub mengembalikan 404 untuk repo privat itu disengaja, karena keberadaan repo itu sendiri sensitif. Tapi jika API Anda mengembalikan 404 untuk “akun user ini ditangguhkan,” sekarang user yang sah tidak bisa membedakan apakah mereka salah ketik username atau ditangguhkan. Dokumentasikan kebijakan Anda secara eksplisit dan terapkan secara konsisten.
4. Memakai 500 sebagai catch default
Framework membuat ini mudah, dan itulah masalahnya. Setiap eksepsi tak tertangani jadi 500 dan alerting Anda tidak bisa membedakan “database mati” dari “user mengirim UUID rusak.” Tangkap error validasi dan lemparkan 400 atau 422. Tangkap NotFound dari ORM Anda dan lemparkan 404. Cadangkan 500 untuk kegagalan yang benar-benar tak terduga, dan saat Anda melemparnya, log request ID agar Anda dapat mengkorelasikan.
5. Rantai redirect yang panjang
Setiap hop memakan satu round trip. Jika /old → /intermediate → /canonical, itu dua DNS lookup tambahan dan dua TCP handshake tambahan (skenario terburuk). Google secara khusus menurunkan prioritas crawl untuk rantai yang lebih panjang dari 3 hop, dan browser membatasi rantai redirect sekitar 20 untuk mencegah loop. Ringkas rantai di sumbernya, baik di konfigurasi CDN maupun di redirect map aplikasi Anda.
Kode status HTTP dan SEO
Mesin pencari memperlakukan kode status sebagai sinyal otoritatif tentang apakah harus mempertahankan, melepas, atau mentransfer URL. Salah memilihnya, peringkat Anda bergeser.
301 vs 302 (link equity)
301 Moved Permanently mentransfer PageRank: Google memperlakukan URL baru sebagai tujuan kanonikal dari semua sinyal yang menunjuk ke URL lama. 302 Found tidak mentransfer link equity (atau mentransfernya dengan lambat, tergantung heuristik Google). Jika Anda mengganti nama URL secara permanen, gunakan 301. Jika Anda mengarahkan tamu ke /login, gunakan 302.
404 vs 410 vs Soft 404
Google membedakan tiga state “missing”:
404 Not Foundberarti Google memeriksa ulang secara berkala dan tetap menyimpan URL di indeks untuk sementara.410 Goneberarti Google melepasnya lebih cepat, sering hanya dalam satu siklus crawl.- Soft 404 adalah istilah Google untuk halaman yang mengembalikan
200 OKtapi merender pesan “not found”. Google mendeteksi ini dari pola konten dan tetap memperlakukannya sebagai404, tapi Anda sudah menyia-nyiakan satu permintaan crawl dan mungkin mengencerkan konten asli Anda.
Jika Anda sedang membersihkan indeks usang, kembalikan 410 sungguhan untuk URL yang dihapus permanen.
5xx dan crawl budget
Crawler Google menurunkan rate-nya saat sebuah situs terus-menerus mengembalikan 5xx. Laporan Crawl Stats di Search Console menunjukkan ini, dan lonjakan error 5xx yang berkelanjutan dapat memangkas crawl budget Anda berhari-hari, yang berarti halaman baru lebih lama untuk diindeks. Perlakukan rasio 5xx sebagai metrik SEO, bukan hanya metrik reliabilitas.
200 OK yang sebenarnya rusak
Mengembalikan 200 OK dengan halaman error (anti-pattern soft-404) adalah skenario terburuk untuk SEO. Google mengindeks pesan errornya, memberikan peringkat untuk hal yang tidak berarti, dan perlahan mencari tahu bahwa halamannya rusak. Selalu kembalikan kode status yang tepat dari server, bahkan saat single-page app Anda merender UI error yang ramah.
Cara memeriksa kode status HTTP (tooling)
Anda tidak bisa memperbaiki yang tidak bisa Anda lihat. Setiap developer yang aktif sebaiknya fasih setidaknya pada tiga di antara berikut.
Panel Network browser DevTools
Chrome, Firefox, dan Safari semuanya menampilkan kolom Status di tab Network. Klik kanan header kolom untuk menambahkan Status Text jika belum tampak. Trik berguna:
- Preserve log menjaga entri lintas navigasi agar Anda dapat melihat seluruh rantai redirect.
- Filter berdasarkan status, ketik
status-code:5xx(Chrome) untuk melihat hanya server error. - Replay XHR, klik kanan permintaan apa pun lalu pilih Replay XHR untuk menjalankannya ulang tanpa me-reload halaman.
Untuk redirect, perluas permintaan untuk melihat setiap hop dan kode status di tiap langkah.
curl (jawaban universal)
curl menampilkan segalanya. Tiga pola yang menyelesaikan 90% kebutuhan debugging:
# Hanya kode status
curl -o /dev/null -s -w "%{http_code}\n" https://api.example.com/users/1
# Hanya header (HEAD request, ikuti redirect)
curl -I -L https://example.com
# Verbose lengkap dengan header request dan response
curl -v https://api.example.com/users/1
Saat Anda menyusun URL uji dengan karakter spesial di query string, gunakan --data-urlencode agar curl yang menangani encoding-nya, atau tempelkan URL ke URL Decoder & Encoder milik kami untuk memverifikasi byte mana yang sebenarnya akan dikirim.
# curl meng-encode nilai query untuk Anda
curl -G "https://api.example.com/search" \
--data-urlencode "q=hello world & friends"
# Sends: GET /search?q=hello%20world%20%26%20friends
JavaScript fetch
Properti Response.status berisi kode integer-nya. Response.ok bernilai true untuk 2xx apa pun.
const res = await fetch('https://api.example.com/users/1');
console.log(res.status); // 200
console.log(res.statusText); // "OK"
console.log(res.ok); // true
if (!res.ok) {
if (res.status === 401) {
// refresh token and retry
} else if (res.status === 429) {
const retryAfter = Number(res.headers.get('Retry-After')) || 1;
await new Promise(r => setTimeout(r, retryAfter * 1000));
} else if (res.status >= 500) {
throw new Error(`Server error: ${res.status}`);
}
}
Di axios, logika yang sama berada di interceptor:
import axios from 'axios';
axios.interceptors.response.use(
response => response,
error => {
const status = error.response?.status;
if (status === 401) {
// redirect to login
}
return Promise.reject(error);
}
);
Python requests
import requests
r = requests.get('https://api.example.com/users/1')
print(r.status_code) # 200
print(r.reason) # 'OK'
# Raises requests.exceptions.HTTPError for 4xx/5xx
r.raise_for_status()
# Manual handling
if r.status_code == 429:
retry_after = int(r.headers.get('Retry-After', '1'))
time.sleep(retry_after)
elif 500 <= r.status_code < 600:
raise RuntimeError(f'Server error: {r.status_code}')
raise_for_status() adalah idiom Python untuk “gagal dengan keras pada 4xx/5xx.” Pakai di skrip yang ingin eksepsi pada error alih-alih bercabang berdasarkan status_code.
Postman dan Bruno
Keduanya memungkinkan Anda mengasersi kode status di dalam test script:
// Postman/Bruno test script
pm.test("Status is 201", () => {
pm.response.to.have.status(201);
});
pm.test("Has Location header", () => {
pm.expect(pm.response.headers.get('Location')).to.match(/^\/users\/\d+$/);
});
Jalankan ini terhadap staging di CI untuk menangkap pelanggaran kontrak sebelum production.
FAQ
Apa perbedaan antara 401 dan 403?
401 Unauthorized berarti server tidak tahu siapa Anda, sehingga kredensial Anda hilang, kedaluwarsa, atau invalid. 403 Forbidden berarti server tahu siapa Anda dan tetap menolak. Jika mengirim kredensial berbeda mungkin memperbaikinya, gunakan 401. Jika tidak, gunakan 403.
Kapan saya sebaiknya pakai 301 vs 302?
Gunakan 301 saat perpindahan permanen, ketika URL lama tidak akan kembali, dan Anda ingin mesin pencari mentransfer link equity ke URL baru. Gunakan 302 untuk redirect sementara di mana URL asli tetap kanonikal (alur login, A/B testing, halaman maintenance). Untuk API, prioritaskan 308 dan 307 karena keduanya mempertahankan method permintaan.
Apa arti error 502 Bad Gateway?
502 berarti reverse proxy atau load balancer menerima respons tidak valid dari server upstream. Upstream membalas, tapi dengan sampah: protokol salah, header rusak, atau koneksi terputus. Berbeda dari 504 Gateway Timeout, di mana upstream sama sekali tidak membalas. Tempat pertama untuk diperiksa: log origin server untuk crash atau respons yang terpotong.
Apa itu “soft 404”?
“Soft 404” adalah halaman yang mengembalikan 200 OK tapi sebenarnya menampilkan pesan “not found”. Google mendeteksinya secara heuristik dan tetap memperlakukannya sebagai 404. Halaman seperti ini memboroskan crawl budget dan dapat mengencerkan konten asli Anda. Selalu kembalikan kode status 404 atau 410 sungguhan dari server, bahkan jika single-page app Anda merender UI error yang ramah.
Kapan saya sebaiknya pakai 422 alih-alih 400?
Gunakan 400 Bad Request saat server bahkan tidak bisa mem-parse permintaan, mis. JSON rusak, field struktural hilang, error sintaks. Gunakan 422 Unprocessable Content saat permintaan ter-parse dengan baik tapi gagal validasi bisnis, mis. format email invalid, nilai di luar rentang, field yang tidak konsisten secara semantik. Singkatnya: 400 untuk sintaks, 422 untuk semantik.
Bagaimana cara merespons 429 Too Many Requests?
Baca header Retry-After (jumlah detik atau HTTP date) dan mundur setidaknya selama itu sebelum mencoba lagi. Jika Retry-After tidak ada, gunakan exponential backoff dengan jitter mulai sekitar 1 detik. Jangan pernah retry segera, karena itu cara cepat untuk diblokir.
Apakah kode informational 1xx masih dipakai di 2026?
Ya, tapi sebagian besar tidak terlihat oleh kode aplikasi. 100 Continue dan 101 Switching Protocols adalah fitur dasar HTTP/1.1. 103 Early Hints semakin sering dipakai oleh Cloudflare, Fastly, dan Vercel untuk mendorong hint preload sebelum respons utama, dan dampaknya pada Largest Contentful Paint terasa nyata. Kebanyakan library HTTP menggabungkan 1xx ke respons akhir, jadi Anda biasanya hanya melihatnya di DevTools atau curl -v.
Apakah 418 “I’m a teapot” itu kode status sungguhan?
Ya, mengejutkan tapi benar. RFC 2324 adalah lelucon April Mop tahun 1998, tapi cukup banyak produk yang mengimplementasikannya hingga IETF tetap mempertahankannya di RFC 7168. Jangan rilis 418 di production, karena banyak reverse proxy dan load balancer tidak menanganinya dengan benar, dan kode ini tidak punya tujuan nyata di luar lelucon.