Cara Membuat File .htpasswd: Panduan HTTP Basic Auth
File .htpasswd adalah tempat penyimpanan kredensial di sisi server untuk HTTP Basic Authentication: berkas teks biasa yang tiap barisnya berisi satu pasangan username:hash. Untuk membuat file .htpasswd, Anda menghasilkan baris ber-hash itu lalu menyimpannya di lokasi yang bisa dibaca server web Anda. Ada tiga cara melakukannya:
- Perintah
htpasswd(dariapache2-utils/httpd-tools) — alat resmi standarnya. openssl passwd— sudah terpasang hampir di mana-mana, tanpa paket tambahan.- Lewat browser — gunakan generator htpasswd untuk membuat entri secara lokal tanpa instalasi apa pun dan tanpa mengirim data lewat jaringan.
Panduan ini tidak berhenti di satu baris perintah. Kita bahas cara kerja handshake Basic Auth yang sebenarnya, tiga metode untuk membuat berkasnya, format hash mana yang sebaiknya dipilih dari lima yang ada, cara menyambungkannya ke Apache, nginx, Docker, Kubernetes, Caddy, dan Traefik, serta cara mengamankan semuanya agar Anda tidak menaruh berkas kredensial yang bisa diunduh siapa saja.
Apa Itu File .htpasswd?
Setiap baris dalam file .htpasswd menyimpan kredensial satu pengguna sebagai pasangan yang dipisahkan titik dua. Username tersimpan apa adanya; password hanya tersimpan sebagai hash satu arah, jadi teks aslinya tidak pernah sampai ke disk. Satu baris bcrypt terlihat seperti ini:
admin : $2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
│ │
└─ username └─ hash (algorithm prefix $2y$ + cost + salt + digest)
Username dulu, lalu satu :, kemudian hash-nya. Sebuah username boleh sampai 255 byte dan tidak boleh memuat titik dua, karena titik dua itulah pemisah antar-field. Hash membawa penanda algoritmanya sendiri sebagai prefiks ($2y$ untuk bcrypt, $apr1$ untuk Apache MD5, {SHA} untuk SHA-1), jadi server tahu cara memverifikasinya tanpa konfigurasi tambahan.
Untuk banyak pengguna, Anda menambahkan satu baris per pengguna:
admin:$2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
alice:$2y$10$3bQ8xY7tLp2mZ0xW5cR4fO9vK1jH6sD2nG8aQ5wE3rT7uI4oP1cm
bob:$apr1$mZ0xW5cR$4fK1jH6sD2nG8aQ5wE3rT2
Anda boleh mencampur algoritma dalam satu berkas yang sama. Server membaca tiap baris, mengenali formatnya dari prefiks, lalu memverifikasi sesuai format itu. Di sini dua pengguna bcrypt dan satu pengguna apr1 hidup berdampingan tanpa masalah.
.htpasswd vs .htaccess
Keduanya kerap tertukar karena selalu berdampingan di Apache, padahal tugasnya berbeda. .htaccess adalah berkas konfigurasi per-direktori milik Apache. Isinya directive — termasuk yang mengaktifkan Basic Auth dan menunjuk ke tempat penyimpanan kredensial Anda. .htpasswd adalah basis data kredensial-nya — hanya baris-baris username:hash, tanpa konfigurasi.
Singkatnya: .htaccess menetapkan bahwa sebuah direktori memerlukan login dan di mana daftar penggunanya berada; .htpasswd adalah daftar pengguna itu. nginx sama sekali tidak memakai .htaccess — konfigurasi Basic Auth-nya ada di blok server atau location pada konfigurasi utama, tetapi ia membaca format kredensial .htpasswd yang sama.
Cara Kerja HTTP Basic Authentication
HTTP Basic Authentication adalah handshake challenge-response yang sudah tertanam dalam spesifikasi HTTP. Pahami ketiga langkahnya, dan setiap langkah troubleshooting berikutnya jadi jelas:
- Klien meminta sebuah resource yang dilindungi tanpa kredensial.
- Server membalas dengan
401 Unauthorizeddan menyertakan headerWWW-Authenticate: Basic realm="..."— inilah tantangannya (challenge). - Klien mengulang permintaan dengan header
Authorization: Basic <base64(user:password)>. Jika kredensial cocok dengan salah satu baris dalam file.htpasswd, server mengembalikan resource tersebut.
Itulah seluruh protokolnya. Tidak ada formulir login, tidak ada cookie sesi, tidak ada token. Setiap permintaan berikutnya membawa header yang sama.
Tantangan 401 / WWW-Authenticate
Header WWW-Authenticate melakukan dua hal. Token Basic-nya memberi tahu klien skema mana yang harus dipakai, dan string realm-nya melabeli ruang proteksi. Browser menampilkan teks realm pada dialog login (“Situs ini menyatakan: …”) dan memakainya sebagai kunci cache: kredensial yang Anda masukkan untuk satu realm dipakai ulang untuk URL lain dalam realm yang sama, jadi pengguna tidak diminta ulang di setiap halaman.
Berikut pertukaran mentahnya, ditangkap dengan curl -i:
$ curl -i https://example.com/admin/
HTTP/2 401
www-authenticate: Basic realm="Restricted Area"
$ curl -i -u admin:s3cret https://example.com/admin/
HTTP/2 200
Header Authorization: Basic
Kredensial yang dikirim klien adalah base64(username:password). Inilah fakta keamanan terpenting tentang Basic Auth: base64 adalah encoding, bukan enkripsi. Siapa pun bisa membalikkannya sepenuhnya, jadi kredensial pada praktiknya berpindah dalam bentuk teks biasa. Anda bisa melihat sendiri perjalanan bolak-baliknya:
# Encode the credential the way a browser does
printf 'admin:s3cret' | base64
# → YWRtaW46czNjcmV0
# Anyone who captures the header can decode it instantly
printf 'YWRtaW46czNjcmV0' | base64 -d
# → admin:s3cret
Karena bisa dibalik itulah Basic Auth harus berjalan di atas HTTPS — tanpa TLS, siapa pun di jalur jaringan bisa membaca password. Kalau Anda ingin menyusun atau memeriksa header tersebut secara manual, encoder/decoder Base64 melakukan transformasi user:password yang sama di dalam browser.
Cara Membuat File .htpasswd
Ada tiga cara praktis untuk membuat berkasnya. Pilih berdasarkan apa yang sudah terpasang dan di mana Anda ingin password itu berada.
Menggunakan perintah htpasswd
Biner htpasswd ikut dalam paket utilitas Apache. Pasang dulu:
# Debian / Ubuntu
sudo apt install apache2-utils
# RHEL / CentOS / Fedora
sudo yum install httpd-tools
Buat berkasnya beserta pengguna pertamanya. Flag -c berarti create, dan ia akan menimpa berkas yang sudah ada — pakai hanya pada saat pertama kali:
htpasswd -c /etc/nginx/.htpasswd admin
# prompts twice for the password, then writes the file
Untuk menambah pengguna lain, lepaskan -c supaya Anda menambahkan (append) alih-alih menimpa:
htpasswd /etc/nginx/.htpasswd alice
Untuk memaksa bcrypt alih-alih default platform, tambahkan -B. Untuk mencetak entri ke stdout tanpa menyentuh berkas apa pun — berguna kalau Anda mau mengalirkannya (pipe) ke konfigurasi atau Dockerfile — gabungkan -b (password di baris perintah) dan -n (tanpa berkas):
htpasswd -Bbn admin 's3cret'
# → admin:$2y$10$N9qo8uLOickgx2ZMRZoMye...
Flag yang akan benar-benar Anda gunakan:
| Flag | Arti |
|---|---|
-c | Buat berkas baru (menimpa jika sudah ada) — hanya untuk pengguna pertama |
-B | Gunakan bcrypt |
-b | Ambil password sebagai argumen baris perintah (tanpa prompt) |
-n | Cetak ke stdout alih-alih menulis berkas |
-D | Hapus pengguna bernama dari berkas |
Satu hal yang perlu diwaspadai dengan -b: password ikut masuk ke riwayat shell Anda. Untuk kredensial produksi sekali pakai, lebih baik pakai bentuk dengan prompt atau opsi browser di bawah ini.
Tanpa apache2-utils — menggunakan OpenSSL
Tidak punya biner htpasswd? OpenSSL ada di hampir setiap sistem dan bisa menghasilkan hash apr1 secara langsung. Bungkus dengan printf untuk menyusun satu baris lengkap:
printf "admin:$(openssl passwd -apr1 's3cret')\n" >> /etc/nginx/.htpasswd
# admin:$apr1$k3l4Hj9.$qN8vY7tLp2mZ0xW5cR4f.
Format apr1 portabel di Apache maupun nginx, jadi inilah rute paling bebas-dependensi pada mesin minimal.
Lewat browser — tanpa instalasi, tanpa kebocoran
Kalau Anda tidak ingin memasang paket apa pun, atau enggan mengetik password produksi ke dalam shell yang akan menyimpannya di ~/.bash_history, hasilkan entri di sisi klien. Generator htpasswd menghitung hash bcrypt, apr1, dan SHA-1 sepenuhnya di dalam browser Anda, memberi baris user:hash siap tempel beserta blok konfigurasi server yang cocok, dan tidak pernah mengirim apa pun ke luar. Selagi di sana, buat juga password kuat dan unik dengan generator password acak alih-alih memakai ulang yang lama.
Perbandingan Format Password htpasswd
Perintah htpasswd bisa menghasilkan lima format, dan kelimanya tidak setara. Tabel ini referensi cepat untuk memilih salah satunya:
| Format | Prefiks | Bersalt | Kekuatan | Pakai untuk |
|---|---|---|---|---|
| bcrypt | $2y$ | Ya | Terkuat | Apache, Docker Registry, Caddy, Traefik |
| apr1 (Apache MD5) | $apr1$ | Ya | Sedang | nginx (portabel, default yang aman) |
| SHA-1 | {SHA} | Tidak | Lemah | Hanya untuk kompatibilitas lama |
| crypt (DES) | (tidak ada) | Ya (2 karakter) | Sangat lemah | Jangan gunakan |
| plain | (tidak ada) | Tidak | Tidak ada | Hanya untuk pengujian lokal |
Beberapa catatan yang tidak muat dalam sel tabel. bcrypt memakai salt acak 16 byte dan faktor cost adaptif (default 10, rekomendasi modern 12), jadi password yang identik tetap menghasilkan hash berbeda dan faktor kerjanya menyesuaikan perangkat keras. Satu keanehannya: bcrypt memotong password pada 72 byte — kelebihannya diabaikan diam-diam. apr1 menjalankan 1.000 putaran MD5 ber-salt; jauh lebih lemah daripada bcrypt, tetapi Apache maupun nginx mengimplementasikannya secara native, dan itulah yang menjadikannya pilihan portabel. SHA-1 tanpa salt, jadi password yang identik menghasilkan digest yang sama dan rainbow table bisa menyerangnya — simpan ini hanya untuk sistem lama. crypt dan plain ada karena alasan historis dan pengujian; keduanya tidak layak dipakai di produksi.
Prefiks $2a$ / $2b$ / $2y$
Anda akan melihat hash bcrypt diawali $2a$, $2b$, atau $2y$. Ketiganya algoritma yang sama, menghasilkan hash setara yang bisa dipertukarkan; huruf versinya hanya sisa peninggalan perbaikan bug lama pada cara sejumlah library menangani karakter high-bit dan panjang string. htpasswd milik Apache menghasilkan $2y$, dan Caddy, Traefik, serta Docker Registry semuanya memverifikasinya dengan benar.
Jika Anda ingin perbandingan yang lebih mendalam antara bcrypt dan alternatif modern, panduan bcrypt vs Argon2 vs scrypt membahas bagaimana algoritma-algoritma hashing password ini berbeda dalam hal cost, kekerasan memori (memory hardness), dan model ancaman.
Mengonfigurasi Basic Auth di Server Anda
Berkas kredensial tidak berbuat apa-apa dengan sendirinya — Anda harus memberi tahu server untuk mewajibkannya. Berikut enam platform.
Apache (.htaccess)
Letakkan ini ke berkas .htaccess di direktori yang ingin Anda lindungi (atau ke blok <Directory> pada vhost Anda):
AuthType Basic
AuthName "Restricted Area"
AuthUserFile /etc/apache2/.htpasswd
Require valid-user
AuthName adalah string realm yang ditampilkan browser; AuthUserFile adalah path absolut menuju berkas kredensial Anda; Require valid-user menerima pengguna mana pun yang terdaftar di dalamnya.
nginx (auth_basic)
nginx menaruh konfigurasinya di blok location atau server — tidak ada .htaccess:
location /admin/ {
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;
}
Muat ulang dengan nginx -s reload. Pakai format apr1 di sini. nginx menyerahkan verifikasi bcrypt ke crypt() sistem, yang gagal pada banyak build (lebih lanjut soal ini di bagian troubleshooting), sedangkan apr1 diverifikasi secara internal di setiap platform.
Docker Registry & Kubernetes ingress-nginx
Backend htpasswd pada Docker Registry privat hanya menerima bcrypt. Hasilkan entrinya, mount, lalu tunjuk registry ke berkas itu:
# Generate a bcrypt entry into a file
htpasswd -Bbn admin 's3cret' > auth/htpasswd
# Run the registry with that file
docker run -d -p 5000:5000 \
-v "$(pwd)/auth:/auth" \
-e REGISTRY_AUTH=htpasswd \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
registry:2
Untuk Kubernetes ingress-nginx, simpan berkasnya sebagai Secret lalu rujuk lewat anotasi:
kubectl create secret generic basic-auth --from-file=auth=./auth/htpasswd
metadata:
annotations:
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"
Perhatikan, kunci Secret-nya harus bernama auth — ingress-nginx mencari persis kunci itu.
Caddy & Traefik
Keduanya mengharapkan hash bcrypt. Caddy memakai directive basic_auth (tempel hash bcrypt-nya, bukan teks aslinya):
example.com {
basic_auth /admin/* {
admin $2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
}
}
Traefik memakai middleware basicauth, dengan pasangan user:bcrypt-hash (escape setiap $ sesuai format konfigurasi Anda):
http:
middlewares:
admin-auth:
basicAuth:
users:
- "admin:$2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy"
Begitu sebuah endpoint dilindungi, pastikan ia berfungsi dari baris perintah. Pembangun perintah cURL menyusun permintaan -u user:pass untuk Anda, jadi Anda bisa mengecek baik 401 maupun 200 yang terautentikasi.
Praktik Keamanan Terbaik
Basic Auth itu sederhana, jadi cara-cara menyalahgunakannya yang sedikit itu pun mudah dikenali.
- Selalu layani lewat HTTPS. Kredensialnya base64 yang bisa dibalik, jadi HTTP biasa membongkar password di jaringan. Terminasikan TLS di depan setiap endpoint yang dilindungi, tanpa pengecualian.
- Simpan berkasnya di luar web root. Kalau
.htpasswdada di direktori yang dilayani, salah konfigurasi bisa membuat orang mengunduhnya. Taruh di lokasi seperti/etc/nginx/.htpasswd, aturchmod 640, dan jadikan miliknya pengguna server web (www-data,nginx) supaya server bisa membacanya sementara akun lain tidak. - Pakai password yang kuat dan unik. Tiap akun harus punya password ber-entropi tinggi sendiri dari generator password acak, tidak pernah dipakai ulang. Kalau Anda ingin tahu arti “cukup kuat” dalam satuan bit, penjelasan entropi password menguraikan perhitungannya.
- Pahami batasannya. Basic Auth tidak punya logout dan tidak punya sesi: browser menyimpan kredensial per realm sampai Anda menutupnya, lalu mengirimnya ulang pada tiap permintaan. Untuk daftar periksa yang lebih luas soal hashing, header, dan validasi, lihat praktik keamanan web terbaik kami.
Mengatasi Kesalahan Umum
nginx: crypt_r() failed (22: Invalid argument)
Ini kegagalan Basic Auth nginx yang paling umum, dan selalu berarti hal yang sama: nginx mencoba memverifikasi hash bcrypt ($2y$) pada libc yang tidak menyertakan skema Blowfish — biasanya musl pada Alpine atau glibc versi lama. Solusinya, buat ulang entri sebagai apr1, yang diverifikasi nginx secara internal di platform mana pun:
printf "admin:$(openssl passwd -apr1 's3cret')\n" > /etc/nginx/.htpasswd
nginx -s reload
Beralih ke base image yang libc-nya mendukung bcrypt juga berhasil, tetapi apr1 lebih sederhana dan portabel.
401 padahal password sudah benar
Saat Anda yakin passwordnya benar tetapi tetap kena 401, telusuri daftar periksa ini secara berurutan:
- Path berkas. Pastikan
AuthUserFile/auth_basic_user_filemenunjuk ke berkas yang sebenarnya (path absolut, tanpa salah ketik). - Izin. Pengguna server web harus dapat membaca berkasnya. Periksa dengan
sudo -u www-data cat /etc/nginx/.htpasswd. - Akhiran baris / encoding. Berkas yang disunting di Windows bisa membawa karakter
\ryang merusak hash. Jalankanfile .htpasswdlalu konversi dengandos2unixkalau perlu. - Cache browser usang. Browser menyimpan kredensial per realm. Coba di jendela privat/incognito untuk menyingkirkan kemungkinan password lama yang masih diingat.
- Hash tidak cocok. Pastikan hash tersimpan memang cocok dengan password — tempel keduanya ke mode verifikasi pada generator htpasswd sebelum menyalahkan konfigurasi.
Kapan TIDAK Menggunakan Basic Auth
Basic Auth adalah alat yang tepat untuk sekumpulan pekerjaan yang sempit: situs staging, jalur admin internal, endpoint artefak CI, dasbor metrik, registry privat. Bebas-dependensi dan cuma butuh dua menit untuk disiapkan.
Tapi ia alat yang salah untuk login produk. Tidak ada logout, tidak ada reset password, tidak ada rate limiting, tidak ada penguncian akun, tidak ada MFA. Kredensial dikirim ulang pada setiap permintaan dan disimpan browser sampai ditutup. Untuk apa pun yang melibatkan pengguna masuk akun, pakai sesi, OAuth, atau OIDC. Jujur soal batasan itulah yang menjaga Basic Auth tetap berguna di tempat yang memang cocok untuknya.
FAQ
Apa sebenarnya isi satu baris dalam file .htpasswd?
Sebuah pasangan username:hash yang dipisahkan titik dua. Hash diawali prefiks algoritma ($2y$ untuk bcrypt, $apr1$ untuk Apache MD5, {SHA} untuk SHA-1), diikuti salt dan digest. Teks password asli tidak pernah muncul dalam berkasnya.
Apa perbedaan antara .htpasswd dan .htaccess?
.htaccess adalah berkas konfigurasi per-direktori milik Apache — ia mengaktifkan Basic Auth dan menunjuk ke tempat penyimpanan kredensial. .htpasswd adalah tempat penyimpanan kredensial itu, berisi baris-baris username:hash. nginx memakai format .htpasswd tetapi mengatur auth di blok server/location-nya, bukan .htaccess.
Bagaimana cara menambah, mengubah, atau menghapus pengguna dalam file .htpasswd?
Untuk menambah atau mengubah pengguna, jalankan htpasswd /path/.htpasswd username tanpa -c — kalau pengguna sudah ada, hash-nya diperbarui. Untuk menghapus satu pengguna, jalankan htpasswd -D /path/.htpasswd username. Pakai -c hanya untuk pengguna pertama, karena ia menimpa seluruh berkas.
Bagaimana browser mengingat kredensial Basic Auth, dan bagaimana pengguna keluar (logout)?
Browser menyimpan kredensial dengan kunci berdasarkan realm dan mengirimnya ulang otomatis pada setiap permintaan yang cocok. Tidak ada logout standar: satu-satunya cara menghapusnya adalah menutup browser atau mengosongkan cache-nya. Ketiadaan logout itulah salah satu alasan Basic Auth tidak cocok untuk autentikasi produk.
Bisakah saya memakai file .htpasswd yang sama untuk Apache dan nginx?
Bisa, selama format hash-nya didukung keduanya. Apache dan nginx sama-sama memverifikasi apr1 (Apache MD5) secara native di mana-mana, jadi itulah pilihan bersama yang paling aman. bcrypt berfungsi di Apache tetapi pada nginx bergantung pada crypt() sistem, yang gagal pada build Alpine/musl.
Apakah HTTP Basic Authentication masih relevan di tahun 2026?
Ya. Sebagai gerbang ringan di atas HTTPS — alat internal, lingkungan staging, registry privat, endpoint pemantauan — ia masih praktis dan bebas-dependensi. Jangan keliru menganggapnya autentikasi produk yang berhadapan dengan pengguna, yang butuh sesi, reset, rate limiting, dan MFA yang tidak bisa disediakan Basic Auth.
Ditinjau oleh tim Go Tools: setiap perintah, blok konfigurasi, dan format hash dalam panduan ini telah diperiksa terhadap keluaran rujukan Apache htpasswd (apache2-utils) dan OpenSSL.