htpasswd, Basic Auth, and Why bcrypt Won
Some corners of the web never needed a login system — just a locked door. A staging site, an admin panel, a client preview, a family photo folder: HTTP Basic Authentication puts a username-and-password prompt in front of any directory, no application code required. The credentials live in a .htpasswd file, and generating its entries correctly — with the right hash, in the right format, without leaking the password to a stranger's server — is a small skill with outsized security consequences. This guide covers what htpasswd is, how the hash algorithms compare, what bcrypt's cost factor does, and how to install everything on Apache.
What htpasswd actually is
When a browser hits a protected directory, the server answers “401 Unauthorized” and the browser pops up its built-in login dialog. The typed credentials travel with each request, and the server checks them against .htpasswd — a plain text file of username:hash lines. The critical detail: the file stores hashes, not passwords. If the file leaks, an attacker gets puzzles, not keys — and how hard those puzzles are depends entirely on which hash you chose.
Historically you made these entries with Apache's htpasswd command on the server itself. But plenty of people who need a protected directory — freelancers on shared hosting, hobbyists, small teams — have no shell access. Online generators fill the gap, with one giant caveat: most of them ask you to send your password to their server. A password destined to guard something should never take a detour through a stranger's logs. The correct architecture is hashing in the browser, which is exactly what ToolsOfWeb's htpasswd Generator does — open devtools and watch the network tab stay silent while it works.
bcrypt vs MD5-APR1 vs SHA1
Three hashes commonly appear in htpasswd files, and they belong to three different eras of password security:
- bcrypt ($2y$) — the modern choice. Designed from the ground up for passwords: deliberately slow, with a tunable cost factor, and a built-in salt per entry. Supported by Apache 2.4+. This is what you should generate for any new setup.
- MD5-APR1 ($apr1$) — the legacy Apache default. An Apache-specific MD5 variant that was standard for years. MD5's speed is its downfall here: GPUs try billions of candidates per second. Acceptable for a throwaway staging site; irresponsible for anything valuable.
- SHA1 ({SHA}) — ancient. One unsalted SHA-1 digest. Kept alive only by very old systems that expect it. Never choose it deliberately.
The mental model: password hashing is a race between your server verifying one login and an attacker guessing millions. Fast hashes (MD5, SHA1) help the attacker far more than they help you. Slow, tunable hashes (bcrypt, and its successors like Argon2) rebalance the race. When someone says “MD5 is broken,” for passwords they mostly mean “it was never slow enough.”
The cost factor, in plain English
bcrypt's cost is an exponent on the work: cost 11 takes twice as long as cost 10, cost 12 twice as long again. In practice, cost 10 verifies a login in well under a second while forcing an attacker to pay that price for every single guess. That asymmetry is the entire security proposition. Rule of thumb: use the highest cost your users will tolerate — 10 by default, 11–12 for anything sensitive. Revisit every couple of years; as hardware improves, yesterday's cost 10 becomes tomorrow's cost 12, and bcrypt lets you re-hash on next login without disrupting anyone.
Two bcrypt quirks worth knowing: input is truncated at 72 bytes (a Blowfish inheritance — irrelevant for normal passwords), and the $2y$ prefix you see in htpasswd files is Apache's spelling of the corrected Blowfish variant (identical to $2b$ elsewhere). Both details are handled for you by the generator.
Installing it on Apache, step by step
- Generate entries with the htpasswd Generator — bcrypt at cost 10, one line per user. Download the .htpasswd file.
- Upload it outside the web root. If your site lives in /var/www/html, put the file in /etc/apache2/ or your home directory — anywhere the web server process can read but browsers can never request.
- Drop the .htaccess snippet into the directory to protect, with AuthUserFile pointing at the file's real path and AuthName set to whatever the login prompt should say. No restart needed.
- Force HTTPS. Basic auth resends credentials with every request; without TLS they travel as trivially-decoded base64. A password file means nothing on plain HTTP.
On Nginx, skip .htaccess entirely: the same .htpasswd file works with auth_basic_user_file plus auth_basic "Restricted" in your location block.
Common mistakes to avoid
- Generating on a server-side website and hoping the password was not logged. Hash locally or not at all.
- Storing .htpasswd inside the document root where a misconfiguration could serve it as plain text.
- Using MD5-APR1 “because it always worked” on infrastructure that supports bcrypt. Legacy defaults are not recommendations.
- Reusing one entry everywhere. Generate a distinct username/password per person or per environment — revoking one shouldn't mean reissuing all.
Start with strong secrets: the Password Generator makes random ones, and the Diceware Passphrase Generator makes memorable ones — then hash them here, in your browser, where they belong.
FAQs
What is an htpasswd file?+
A text file of "username:hash" lines that Apache (and Nginx) read to decide who may enter a password-protected directory. Passwords are stored as one-way hashes — the server never sees plain text, and neither should anyone generating the file.
Why is bcrypt better than MD5 for htpasswd?+
MD5 was designed to be fast, which lets attackers test billions of passwords per second on a GPU. bcrypt was designed to be slow and tunable: its cost factor makes each guess expensive. A stolen bcrypt file at cost 10+ buys years; a stolen MD5 file buys hours.
What bcrypt cost should I use?+
Cost 10 is the sensible default — under a second to verify a login, expensive to attack. Use 11–12 for high-value accounts if your server handles the delay gracefully. Below cost 8 you are giving away most of bcrypt’s advantage.
Does Nginx use htpasswd files too?+
Yes. Nginx does not read .htaccess, but its auth_basic_user_file directive accepts the same htpasswd-format file. Generate once, use on either server.
Should the .htpasswd file live inside the website folder?+
No. Keep it outside the public web root whenever possible so it can never be downloaded over HTTP by mistake. Point AuthUserFile (Apache) or auth_basic_user_file (Nginx) at its real filesystem path.
