How to Create a .htpasswd File for Apache and Nginx Basic Auth
Build a .htpasswd file for Apache or Nginx basic auth in your browser. Learn the user:hash format, when to pick bcrypt over apr1 or SHA-1, and how to wall off a staging site.
How to Create a .htpasswd File for Apache and Nginx Basic Auth
Basic Auth is the oldest trick in the web server book, and it still earns its keep. When you need a password prompt in front of a directory, a staging domain, or a single admin path, you do not have to stand up a login system, a session store, or an OAuth flow. You drop one file on the server, add two lines of config, and reload. That file is .htpasswd, and this guide walks through what goes in it, which hashing scheme to pick, and how to generate the entries without ever sending a password to a server.
What a .htpasswd file actually contains
A .htpasswd file is a plain text list, one user per line. Each line follows a single format: username:hashed-password. The username sits before the colon in the clear, and everything after the colon is the hash of the password — never the password itself. A small file with two users looks like this:
alice:$2y$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
bob:$apr1$ZxQ9.tR2$qB3w1lJ8c2eK4dF6gH8iI0
When a browser sends a request to a protected path, it includes the username and password the visitor typed into the login box. The server looks up the username, takes the submitted password, hashes it with the same scheme and salt recorded in the stored hash, and compares the two. Match means a 200 and the page loads; no match means a 401 and the browser shows the prompt again. The original password is never stored and never needs to be — the server only ever compares hashes.
Because the format is one row per user, maintenance is trivial. To add a person you append a line. To revoke access you delete their line. There is no database migration and no restart of an auth service — Apache and Nginx reread the file on the next request.
bcrypt vs apr1 vs SHA-1
The part after the colon can be produced by different hashing schemes, and the prefix tells you which one. This is the decision that actually matters for security, so here is the short version: pick bcrypt unless something forces you off it.
- bcrypt (
$2y$) is the strongest option. It is deliberately slow to compute and salted, which is exactly what you want from a password hash — slowness is the feature that makes brute-forcing expensive. Apache 2.4+ and modern Nginx both verify it. - apr1 (
$apr1$) is the Apache legacy default. It is salted MD5, which is what the oldhtpasswd -mcommand produces. It still works everywhere, but MD5 is weak, so reach for it only when your server is too old to verify bcrypt. - SHA-1 (
{SHA}) is fast and unsalted. That combination is a liability: two users with the same password produce the identical hash, and a rainbow table cracks it in moments. It exists purely for legacy systems that demand it.
A useful tell: bcrypt and apr1 prepend a random salt, so hashing the same password twice gives two different strings — both valid, both verifiable. SHA-1 has no salt, so the same password always yields the same output. If you ever see two identical hashes in a file, you are looking at SHA-1 and a shared password, which is a quiet red flag.
A worked example
Say you want to add a user called team. You type the username, type a strong password, leave the scheme on bcrypt, and the generated line comes out shaped like this:
team:$2y$10$kP7qR.W2zXe1NfYbV8oUaeJ3mLdQ9sTcH5gB6iWnA0pK4rE2xZ.O
Read the hash left to right and it tells its own story. $2y$ marks it as bcrypt. 10$ is the cost factor — the number of rounds, where higher means slower and harder to crack. The next 22 characters are the salt, and the remaining string is the actual digest. The whole thing after the colon is what gets stored. If you regenerate it, the salt changes and so does the digest, but the server will still authenticate the same password against any of those values — that is the salt doing its job.
A note on local hashing
Here is the part that matters when you are about to type a real production password into a web form: with the .htpasswd Generator, the hashing happens entirely in your browser. The bcrypt, apr1, and SHA-1 routines all run as plain JavaScript in the tab. Your username, your password, the random salt, and the generated line never touch the network, are never logged, and are deliberately kept out of the URL — there is no "share with result" link for credentials, by design. The only thing remembered between visits is which scheme you last chose.
I lean on this constantly. When I have to spin up a quick password wall on a box I am SSH'd into, I do not want to install or remember the htpasswd CLI flags, and I definitely do not want to paste a password into some random API-backed site. Generating the line locally and copying it straight into the file is faster and it keeps the secret on my own machine the whole way. The first time I checked the network tab while generating a hash and saw exactly zero requests fire, I stopped second-guessing it.
Protecting a staging site, step by step
The most common reason to reach for this is a staging environment you want walled off — both so Google does not index it and so curious visitors cannot poke around. On Nginx the recipe is:
- Generate a one-user line (username
team, a strong password, scheme bcrypt) and save it to/etc/nginx/.htpasswd. Keep the file above your web root so the server never serves it as a downloadable file. - In the relevant
serverblock, addauth_basic "Staging";andauth_basic_user_file /etc/nginx/.htpasswd;. - Reload Nginx.
Now every request to that host gets a login prompt, and crawlers get a 401 instead of your unreleased pages. On Apache the equivalent goes in .htaccess: AuthType Basic, AuthName "Restricted", AuthUserFile /etc/apache2/.htpasswd, and Require valid-user. If you want help assembling the surrounding rules — redirects, headers, caching directives — pair this with the .htaccess Generator so the whole file comes together in one place.
Common mistakes to avoid
Three things trip people up. First, do not put the .htpasswd file inside the web root. If it sits under a path the server publishes, anyone can download your hashes and start cracking them offline — keep it somewhere like /etc/apache2/.htpasswd and point AuthUserFile at the absolute path. Second, do not pick SHA-1 just because it looks shortest; it is unsalted and falls to a rainbow table instantly. Third, remember that one row equals one user. Appending a line adds a person; duplicating a username means the server uses only the first matching row and silently ignores the rest.
Basic Auth will not replace a real identity system for a consumer product. But for a staging gate, an internal /metrics endpoint, or a quick admin wall, it is the fastest credible lock you can ship — one file, two config lines, and a hash that never left your laptop.
Made by Toolora · Updated 2026-06-13