401 Unauthorized adalah salah satu kode status HTTP dengan penamaan terburuk. Ia tidak berarti Anda kekurangan izin — itu 403. Ia berarti server sama sekali tidak bisa memastikan Anda siapa, dan sedang mengundang Anda membuktikannya.
Perbedaan itu menentukan ke mana Anda harus melihat. 401 adalah masalah autentikasi: kredensial yang hilang, kedaluwarsa, salah format, atau ditolak. 403 adalah masalah otorisasi: identitas sudah ditetapkan dan memang tidak diizinkan. Memperbaiki 401 berarti memperbaiki cara kredensial dikirim; memperbaiki 403 berarti mengubah sebuah aturan.
Panduan ini membahas penyebab yang nyata, satu header wajib yang sering dilupakan, dan satu perilaku proxy yang diam-diam merusak autentikasi pada kode yang sudah sepenuhnya benar.
401 vs 403 dan kode tetangganya
Kode-kode ini dipakai secara tertukar di banyak API nyata, dan itulah justru alasan proses debug terasa membingungkan. Berikut arti yang seharusnya.
| Kode | Arti sebenarnya | Bisa diperbaiki pemanggil? |
|---|---|---|
| 401 Unauthorized | Belum terautentikasi — identitas tak dikenal atau ditolak | Bisa — kirim kredensial yang valid |
| 403 Forbidden | Sudah terautentikasi, tapi tidak diizinkan | Tidak — pemilik harus mengubah aturan |
| 407 Proxy Authentication Required | Proxy di tengah meminta kredensial | Bisa — autentikasi ke proxy |
| 419 / 440 (non-standar) | Sesi atau token CSRF kedaluwarsa | Bisa — muat ulang halaman atau ambil token baru |
Header yang semua orang lupakan
Spesifikasi HTTP menyebutkan dengan tegas: respons 401 wajib menyertakan header WWW-Authenticate yang memberi tahu klien cara berautentikasi. 401 tanpa header itu secara teknis adalah respons yang tidak valid.
Pada praktiknya banyak API mengembalikan 401 polos dengan body JSON tanpa header tersebut. Padahal browser dan HTTP client mengandalkannya untuk tahu langkah berikutnya — header inilah yang membuat browser memunculkan kotak nama pengguna dan kata sandi bawaan untuk basic auth.
Kalau Anda sedang membangun API, kirimkan: "WWW-Authenticate: Bearer" untuk autentikasi token, atau "WWW-Authenticate: Basic realm=..." untuk basic auth. Kalau Anda sedang mendebug API orang lain, ketiadaan header ini menandakan 401 tersebut ditulis manual di kode, bukan berasal dari lapisan auth standar — petunjuk berguna tentang di mana harus mencari.
Penyebab umum, dari yang paling sering
- Token kedaluwarsa. Access token memang dirancang berumur pendek; solusinya memperbaruinya, bukan memperpanjang masa berlakunya.
- API key salah atau tidak dikirim. Pastikan Anda memakai key untuk lingkungan yang benar — key staging ke endpoint produksi menghasilkan persis error ini.
- Format header Authorization salah. Kapitalisasi "Bearer" yang keliru, spasi yang hilang, atau karakter baris baru yang ikut tersalin — semuanya gagal tanpa pesan jelas.
- Basic auth salah konfigurasi di server. Path ke .htpasswd keliru, atau file kata sandi dibuat dengan format hash yang tidak bisa dibaca server.
- Sesi kedaluwarsa. Pada website biasa ini hanyalah kasus "Anda telah keluar" yang normal, bukan bug.
- Selisih jam server saat memakai JWT. Kalau jam berbeda melebihi toleransi token, token yang valid pun ditolak sebagai belum berlaku atau sudah kedaluwarsa. Periksa waktu di kedua mesin.
- Header Authorization hilang di perjalanan — dibahas di bagian berikutnya, karena inilah yang paling banyak memakan waktu.
Jebakan: proxy yang menelan header Authorization
Bagian ini layak dapat bab sendiri karena kodenya benar, kredensialnya benar, tapi tetap mengembalikan 401.
Sebagian konfigurasi reverse proxy dan CGI membuang header Authorization sebelum sampai ke aplikasi. Apache dengan mod_cgi dan PHP-FPM adalah kasus klasik: kalau CGIPassAuth tidak diaktifkan atau header tidak diteruskan secara eksplisit, PHP tidak pernah melihatnya, dan setiap permintaan yang sudah terautentikasi tampak anonim.
Gejalanya khas: permintaan yang sama berhasil kalau dikirim langsung ke port aplikasi, tapi gagal lewat URL publik. Kalau Anda bisa mereproduksi perbedaan itu, pelakunya lapisan proxy, bukan kode autentikasi Anda.
Cara memastikan: catat header mentah yang benar-benar diterima aplikasi. Kalau Authorization tidak ada di sana padahal klien jelas mengirimnya, ada sesuatu di antaranya yang menghapusnya. Di Apache tambahkan "CGIPassAuth On"; di Nginx pastikan blok proxy_pass tidak menyaringnya; di hosting terkelola, tanyakan ke support apakah header itu diteruskan.
Periksa dengan urutan ini
- Pastikan kredensialnya memang terkirim. Catat header di sisi klien, atau gunakan "curl -v" — flag -v mencetak apa yang dikirim.
- Uji kredensial yang sama dengan curl langsung ke endpoint. Kalau curl berhasil sementara aplikasi Anda tidak, masalahnya ada di klien, bukan server.
- Pastikan token belum kedaluwarsa. Kalau JWT, dekode dan baca klaim exp — jangan berasumsi, lihat nilainya.
- Bandingkan jam server kalau melibatkan JWT. Selisih beberapa menit saja sudah membuat token valid ditolak.
- Catat header yang benar-benar diterima server. Di sinilah header Authorization yang terpotong akan terlihat.
- Periksa format yang diminta endpoint. Ada API yang mau "Bearer <token>", ada yang mau header "X-API-Key", ada yang lewat query parameter — mengirim nilai benar dengan cara salah tetap menghasilkan 401.
- Baca body responsnya. Banyak API menyisipkan kode error spesifik di JSON yang membedakan kedaluwarsa, tidak valid, dan dicabut — tiga masalah berbeda dengan tiga solusi berbeda.
Kalau Anda yang membangun proteksinya
Ketika Anda yang mengeluarkan 401, beberapa keputusan berikut membuat hidup semua orang jauh lebih mudah.
Selalu kirim WWW-Authenticate seperti di atas. Ia wajib, dan itulah yang memberi tahu klien langkah selanjutnya.
Bedakan "kedaluwarsa" dari "tidak valid" di body. Token kedaluwarsa memberi tahu klien untuk memperbarui; token tidak valid memberi tahu klien untuk autentikasi ulang dari awal. Mengembalikan pesan kabur yang sama untuk keduanya memaksa klien menebak.
Jangan pakai 401 untuk menyembunyikan keberadaan sumber daya. Kalau ingin menyembunyikan ada atau tidaknya sesuatu, kembalikan 404 — itu praktik yang lazim. Sebuah 401 justru mengonfirmasi ada sesuatu di sana yang layak dicoba.
Batasi jumlah percobaan autentikasi yang gagal. Endpoint yang mengembalikan 401 persis menjadi sasaran alat penebak kredensial, dan endpoint login tanpa batas adalah undangan terbuka.
Pakai basic auth hanya di atas HTTPS. Basic auth mengirim kredensial dalam bentuk yang mudah didekode; lewat HTTP polos itu praktis sama dengan mengirim kata sandi terbuka.
Perlu melihat header yang benar-benar diterima server Anda?
Cloud VPS NVMe dengan akses root penuh — log sungguhan, atur sendiri proxy dan autentikasi. Mulai ฿150/bulan.
Pertanyaan yang Sering Diajukan
Apa beda 401 dan 403?
401 berarti server tidak tahu Anda siapa — autentikasi gagal atau tidak pernah diberikan, dan mengirim kredensial valid bisa memperbaikinya. 403 berarti Anda sudah dikenali tapi tetap tidak diizinkan, jadi mengganti kredensial tidak membantu. HTTP menamai keduanya terbalik: 401 sebenarnya "belum terautentikasi" dan 403 adalah "tidak berwenang".
API key saya benar tapi tetap 401. Kenapa?
Periksa tiga hal berurutan: apakah header benar-benar sampai ke server (catat header yang diterima), apakah Anda memakai skema yang diminta API (Bearer / X-API-Key / query parameter), dan apakah key itu milik lingkungan yang Anda panggil. Key staging ke endpoint produksi mengembalikan 401 tanpa petunjuk lain.
Kenapa autentikasi jalan di lokal tapi gagal di produksi?
Sangat sering karena reverse proxy atau lapisan CGI membuang header Authorization sebelum aplikasi melihatnya. Apache dengan PHP adalah kasus klasik dan butuh CGIPassAuth diaktifkan. Catat header mentah yang diterima aplikasi di kedua lingkungan lalu bandingkan — perbandingan itu biasanya menemukannya dalam hitungan menit.
Apakah 401 memengaruhi SEO?
Hanya kalau muncul di halaman yang seharusnya publik. Googlebot tidak bisa berautentikasi, jadi halaman di balik 401 tidak akan terindeks — dan itu perilaku yang benar untuk area privat. Masalah muncul saat aturan auth terlalu luas sampai menutupi halaman publik; pakai URL Inspection di Search Console untuk melihat apa yang benar-benar diterima Googlebot.
GUIDES
Artikel terkait
Lanjut membaca topik serupa
403 Forbidden — Artinya dan Cara Mengatasinya
403 bukan berarti server rusak. Server paham persis apa yang Anda minta, menemukan filenya, lalu memutuskan Anda tidak boleh mengaksesnya. Penolakan itu disengaja.
Baca selengkapnya429 Too Many Requests — Siapa yang Membatasi Anda?
Pertanyaan pertama bukan bagaimana memperbaikinya, melainkan siapa yang membatasi. Server Anda sendiri, CDN di depannya, dan API yang Anda panggil sama-sama mengembalikan 429 — tapi solusinya berbeda untuk masing-masing.
Baca selengkapnya500 Internal Server Error — Penyebab dan Cara Mengatasinya
500 adalah error paling tidak informatif di web: artinya "ada yang salah dan saya tidak akan bilang apa". Kabar baiknya, server hampir selalu menuliskan alasan sebenarnya ke sebuah file log. Panduan ini menunjukkan di mana log itu dan urutan pemeriksaan yang paling cepat menemukan biangnya.
Baca selengkapnya