Htpasswd Generator_
Two different things get called the password here, and only one of them is protected. The .htpasswd file stores a one-way hash, and which algorithm you pick is the entire security of it — htpasswd still offers unsalted SHA-1, and DES crypt, which silently discards everything after the eighth character.
The request is the other half. Basic auth sends the password itself, base64-encoded, on every single request. Base64 is not encryption. That is why decrypt htpasswd is asking for something impossible while decrypt basic auth takes one line — and both are shown here.
What the browser sends on every request
- Input
- A username and a password, and an algorithm. bcrypt takes a cost factor; the rest take a salt where they support one.
- Output
- A user:hash line for the file, and the Authorization header those same credentials produce — the protected form and the unprotected one, side by side.
- Processing
- Hashed in this tab. bcrypt is the implementation already used by the bcrypt generator, checked against the canonical vectors; APR1 is md5crypt with Apache's magic string.
- Limits
- It writes lines. It cannot tell you whether your server reads them, and it will not generate DES crypt at all.
- Why DES crypt is offered for recognition and not for writing
- It truncates the password at eight characters. Everything after the eighth is discarded before hashing, so a long passphrase is worth exactly its first eight letters and two different long passwords can be identical as far as the file is concerned. A thirteen-character line with no prefix is one of these, and it is worth replacing whatever is behind it.
The file is hashed; the request is not
Base64 is not encryption
Basic auth works by sending Authorization: Basic followed by the base64 of username:password. That is the whole mechanism. There is no challenge, no nonce and no key — the password travels in a reversible encoding on every request for as long as the session lasts, and anyone who can read one request can read the password. Over HTTPS the transport protects it; over HTTP nothing does, and no amount of care about the hash in the file changes that.
Which is why the two searches are opposites
People search for decrypt htpasswd and for decrypt basic auth, and the questions look identical. The first is impossible: the file stores a one-way hash, and the only way back is to guess passwords and hash them until one matches. The second is trivial: paste the base64 into anything that decodes it. If you have a hash you have to attack it; if you have a header you have already got the password.
DES crypt truncates at eight characters
The oldest format htpasswd supports has no prefix at all — thirteen characters of the crypt alphabet — and it discards everything after the eighth character of the password before hashing. correcthorse and correcthorsebatterystaple produce the same line. A user who chose a long passphrase has the security of its first eight letters and no way of knowing, which is why this tool recognises the format and refuses to write it.
SHA-1 here is unsalted
The {SHA} format is a plain SHA-1 digest with no salt whatsoever. Two accounts that chose the same password produce byte-identical lines, so the file leaks which users share a password, and a precomputed table breaks any common one instantly. It exists because it was fast to check in 1996. It should be recognised and replaced, never written.
APR1 is better and still fast
Apache's own format is salted MD5 with a thousand iterations, and it is still the default in many htpasswd builds — which is the only reason to know it. A thousand MD5 rounds was expensive in 1994 and is now roughly a million times cheaper to attack than bcrypt at cost 10. If a file you inherited is full of $apr1$ lines it is not an emergency, and it is a good reason to reissue them.
Where the file goes matters as much as what is in it
A .htpasswd inside the document root is downloadable unless the server has been told to refuse, and the leading dot protects nothing — it is a Unix display convention, not an access rule. Put it above the web root and point AuthUserFile at an absolute path. index of htpasswd is a search people run deliberately, and the files it finds are hashes that can be attacked offline at whatever rate the attacker's hardware allows.
Write the line, then look at the header
- 01Enter the username and password. bcrypt is selected because it is the only option here worth using.
- 02Leave the cost at 10 unless you have measured something. Higher is slower for an attacker and for nobody else — the check happens once per request either way.
- 03Copy the line into the file. One user per line, and the file belongs above the document root with AuthUserFile pointing at an absolute path.
- 04Look at the header underneath. That is what travels on every request, and it is reversible — which is the argument for HTTPS rather than for a better hash.
- 05Paste an existing line into the inspector to find out which algorithm it uses. A thirteen-character line with no prefix is the one to replace first.
A staging site behind a password
The commonest use of basic auth, and the one where the file usually ends up in the document root because that was easier.
admin:$2y$10$…
AuthUserFile /etc/apache2/.htpasswd Above the web root. The dot in the name protects nothing.
A file you inherited
Thirteen characters, no prefix. It is DES crypt, and every password in it is worth its first eight letters.
admin:rQGSl0AXQFa0k
Truncated at 8 characters. Reissue every line — you cannot tell which passwords were longer.
A header in a log or a proxy config
It looks encoded and it is not encrypted. Anyone who can read the log has the password.
Basic YWRtaW46aHVudGVyMg==
admin:hunter2 No key, no effort. This is why basic auth needs HTTPS.
Two accounts, one password
Under {SHA} the lines are byte-identical, so the file itself reveals that two users share a password.
{SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=Different lines, because each has its own salt. That is what a salt is for.
The four formats htpasswd knows
| Format | Looks like | Verdict |
|---|---|---|
| bcrypt | $2y$10$… | Use this. Salted, and deliberately slow by a factor you choose |
| APR1 | $apr1$…$… | Avoid. Salted MD5, a thousand rounds — still the default in many builds |
| SHA-1 | {SHA}… | Never. Unsalted. Identical passwords produce identical lines |
| DES crypt | rQGSl0AXQFa0k | Never. Truncates at eight characters. No prefix — recognised by its length |
| Plain text | hunter2 | Windows and Netware only, and exactly what it looks like |
Only bcrypt and APR1 are salted. The salt is what stops two accounts with the same password producing the same line, and what makes a precomputed table useless — it is not about making one hash harder, it is about making every hash a separate problem.
Working with basic auth
- Use bcrypt, and only bcrypt. Every other format htpasswd offers is a compatibility artefact, and two of them are actively dangerous.
- Serve it over HTTPS or do not serve it. The password is in every request in a reversible encoding, so without transport security the hash in the file is decoration.
- Keep the file above the document root. The leading dot is a display convention, not protection, and a downloadable hash file can be attacked at leisure.
- Replace any thirteen-character line on sight. It is DES crypt, and neither you nor the user can tell whether their password was longer than eight characters.
- Do not reuse an application password here. Basic auth credentials end up in shell history, proxy configs, CI variables and logs far more often than passwords normally do.
- Expect no logout. There is no session and no way to expire one — the browser simply keeps sending the header, which is another reason to treat these as low-value credentials.
Where basic auth goes wrong
The password is in every request, encoded rather than encrypted
Base64 is reversible by anyone. Over HTTP the credentials are effectively plaintext, repeated indefinitely, and the strength of the hash in the file is irrelevant to that.
DES crypt discards the password after eight characters
Silently, before hashing. A user who chose a long passphrase gets the security of its first eight letters, and nothing anywhere reports it. Any line of thirteen bare characters is one of these.
Unsalted SHA-1 makes the file leak
Two accounts with the same password produce the same line, so the file itself shows who shares a password — and a precomputed table breaks common ones with no work at all.
A .htpasswd in the web root is downloadable
The dot in the filename is not an access rule. Unless the server refuses it explicitly, the file can be fetched and the hashes attacked offline for as long as anybody likes.
There is no logout
Basic auth has no session to end. Browsers keep sending the header until they are closed, which means credentials cannot be revoked from the application side at all.
Formats, salts and cost
- File format
- One user per line, username and hash separated by a colon — which is why a username cannot contain one. Apache reads all four hash formats; nginx reads whatever the system crypt does, which on Linux with glibc includes bcrypt and APR1.
- bcrypt
- The $2y$ prefix Apache writes, which is the same algorithm as $2b$ under an older name. Cost is a power of two: cost 10 is 1,024 key-setup rounds and each step up doubles it.
- APR1
- md5crypt with Apache's magic string, an eight-character salt and a thousand iterations, ending in md5crypt's own base64 permutation rather than RFC 4648. Verified here against the canonical md5crypt vector, since the two differ only in that magic string.
- SHA-1
- Base64 of a raw SHA-1 digest, prefixed {SHA}. No salt and no iteration count — the format has nowhere to put either.
- DES crypt
- Two salt characters and eleven of digest, thirteen in total with no prefix. Truncates the password at eight bytes. Recognised here and never generated.
- The header
- Base64 of username:password after the word Basic. Only the first colon separates them, so a password may contain colons and a username may not.
- Network
- None from tool code. A test sweep calls every function this page uses with
fetchandXMLHttpRequestreplaced by stubs that throw, so a stray request fails the build instead of shipping. Disconnect from the network and the page still works.
Questions about htpasswd and basic auth
How do I create a .htpasswd file?
One line per user, in the form username:hash, saved above the document root with AuthUserFile pointing at its absolute path. Use bcrypt for the hash — it is the only format htpasswd offers that is worth using today.
Can I decrypt an htpasswd hash?
No. The file stores a one-way hash, and the only route back is guessing passwords and hashing each one until a line matches. How fast that goes depends entirely on the format: bcrypt at cost 10 is slow by design, while unsalted SHA-1 falls to a lookup table immediately.
How do I decode a basic auth header?
It is base64 of username:password — paste it above and it comes straight back. There is no key and nothing to break, which is exactly why basic auth requires HTTPS: the credentials are encoded for transport, not protected.
Which htpasswd algorithm should I use?
bcrypt. APR1 is salted MD5 and roughly a million times faster to attack; {SHA} is unsalted SHA-1 and falls to a precomputed table; DES crypt truncates the password at eight characters. Only bcrypt is a deliberate password hash.
Why does my long password not work any better?
If the line is thirteen bare characters with no prefix, it is DES crypt, which discards everything after the eighth character before hashing. Two different long passwords beginning with the same eight letters are the same password as far as that file is concerned.
Is basic auth secure?
Over HTTPS it is adequate for low-value access such as a staging site. Over HTTP it is not secure at all — the password is sent base64-encoded on every request, which anyone in the path can read. It also has no logout and no way to expire a session.
Where should the .htpasswd file live?
Above the document root, always. Inside it the file is downloadable unless the server explicitly refuses, and the dot at the start of the name is a display convention rather than any kind of protection.
What bcrypt cost should I use?
Ten is the usual default and a reasonable place to start. Each increment doubles the work, for the attacker and for your server equally — and since the check runs once per request, the cost you can afford depends on how often people authenticate rather than on how many users you have.
Does nginx support bcrypt in htpasswd?
Wherever the system crypt does, which on Linux with glibc means yes. nginx delegates to crypt(3) rather than implementing the formats itself, so support follows the C library rather than nginx's own version.
Are the credentials I type here sent anywhere?
No. The work is JavaScript running in this tab. Every function it calls is covered by a test that stubs fetch and XMLHttpRequest to throw, so a request that slipped in would break the build rather than reach a server — and you can confirm it for yourself by disconnecting and carrying on.