lock Apache · Nginx · Basic Auth

.htpasswd Generator

Generate secure password hashes for Apache and Nginx basic authentication. bcrypt with adjustable cost, APR1 MD5, SHA-1. Multiple users in one pass. Hash verification included. Nothing leaves your browser.

enhanced_encryption bcrypt cost 4–14 group_add Batch users verified Verify hashes lock 100% local
key

Generate .htpasswd entries

Salted and slow. Works on Apache 2.4+ and modern Nginx builds.
Each step doubles the work. Higher cost slows attackers but adds login latency.
Generated entries will appear here.

lock All hashing runs locally in your browser. Passwords never touch a server.

verified

Verify an existing hash

Paste a line from an existing .htpasswd file and enter the password to check whether the hash was generated by that password. Useful for confirming a hash copied from a server or generated by another tool.

Enter a hash and password to test.
verified_user

Nothing leaves your device. Every hash is computed in JavaScript on your machine. There is no upload step, no server-side logging, and no retention after you close the tab. If you want to confirm this, load the page, disconnect from the internet, and generate a hash — it will still work.

What an .htpasswd file actually is

An .htpasswd file is a plain text file where each line is one user, in the form username:passwordhash. Apache and Nginx read this file when a protected URL is requested and compare what the browser sends in the Authorization header against what is stored. The plaintext password is never kept in the file — only a one-way digest of it.

A typical file with two users looks like this:

alice:$2y$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewKyY3qVOi7aetK2
bob:$apr1$rOioh4Wh$bVD3DRwksETubcpEH90ww0

The prefix of the hash tells the server which algorithm to use. $2y$ (or $2a$, $2b$) is bcrypt, $apr1$ is Apache's own MD5 variant, and {SHA} is unsalted SHA-1. A single file can mix formats — Apache and Nginx read the prefix and delegate to the right algorithm per line. That is useful when migrating an old file to a newer algorithm without forcing every user to reset their password.

Which algorithm to pick in 2026

The choice is narrower than it looks. Only bcrypt is still considered secure by current standards. Everything else exists for compatibility with older software.

Format Prefix Salted Nginx support Use when
bcrypt $2y$ Yes Via system crypt() Any new deployment. This is the default recommendation.
APR1 MD5 $apr1$ Yes (weak) Native Nginx on Alpine or any build where bcrypt via crypt() is unavailable. Also useful for maximum portability.
SHA-1 {SHA} No Native Only legacy systems that cannot read anything else. Identical passwords produce identical hashes, which is dangerous.
DES crypt (13 chars) Weak Legacy Not recommended. Truncates the password to 8 characters and is trivially crackable on modern hardware.

What bcrypt cost actually controls

The cost parameter is a power of two. Cost 10 means 210 = 1,024 hashing rounds. Cost 12 means 4,096. Cost 14 means 16,384. Each step doubles the work required to compute the hash, which doubles both the attacker's time and the legitimate login delay.

OWASP recommends a minimum of 10 and 12 or higher when latency allows. At cost 10 a modern server produces about 10 hashes per second per core. At cost 14 it produces about 0.6. For a login form where users expect instant response, cost 12 is a common sweet spot — roughly 50 to 100 milliseconds of delay per login, which is unnoticeable to the user but 4,096 times slower for an offline attacker than cost 1.

What the generated entry does not do

Hash generation is only one part of the setup. Two other pieces must be handled correctly for the file to be useful and safe:

Server setup after generating the file

Copy the generated lines into your .htpasswd file. If the file does not exist yet, create it — the tool's download button writes it for you. Then point your web server at it.

Apache

In your virtual host or a .htaccess file inside the protected directory:

<Location "/admin">
  AuthType Basic
  AuthName "Restricted"
  AuthUserFile /etc/apache2/.htpasswd
  Require valid-user
</Location>

Reload with apachectl graceful or systemctl reload apache2.

Nginx

In your server or location block:

location /admin {
  auth_basic "Restricted Area";
  auth_basic_user_file /etc/nginx/.htpasswd;
}

Run nginx -t first to validate the config, then reload with nginx -s reload.

Docker Registry

The same .htpasswd file works for a private Docker registry:

docker run -d \
  -p 5000:5000 \
  -v /etc/docker/auth:/auth \
  -e "REGISTRY_AUTH=htpasswd" \
  -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
  -e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \
  registry:2

Bcrypt is the recommended format for the registry. APR1 also works but is weaker.

Common mistakes that break or weaken the setup

Forgetting the trailing newline

Every entry in an .htpasswd file must end with a newline character. If you generate a single line and paste it without a trailing newline, the next entry you append will concatenate onto the end of the first and both users will stop working. This is a common cause of "my password stopped working after I added a second user." The download button in this tool writes each line with a newline for you.

Basic auth over plain HTTP

Basic Authentication sends the username and password base64-encoded in the Authorization header on every single request. Base64 is not encryption. Anyone capturing packets on the network path — a coffee shop Wi-Fi, a corporate proxy, a compromised router — can decode the credentials in under a second. Basic auth must always be terminated behind HTTPS.

Using APR1 because that is what the tool defaults to

The command-line htpasswd utility still defaults to APR1 when you run it without a flag. Many tutorials therefore show APR1 in their examples, and it has become the de facto default even though it is noticeably weaker than bcrypt. On a new Apache install, run htpasswd -B instead, or use this generator with bcrypt selected.

Bcrypt on Alpine Nginx

Nginx does not implement bcrypt itself. It passes the hash to the system's crypt() function. Alpine Linux uses musl libc, and older builds of musl do not support bcrypt through crypt(). If you are running the official nginx:alpine image, recent versions work, but a custom build may not. When bcrypt fails on Nginx, APR1 MD5 is the correct fallback — Nginx implements it natively without relying on the system library.

World-readable .htpasswd

A .htpasswd file readable by every user on the system is a liability. Any compromised application on the same host can read it and run an offline crack. The file should be readable only by the web server user and root. On shared hosting this is usually handled for you, but on a VPS or container you have to set it yourself.

Khalil Rahman

SkillsInsider Editorial

Reviewed and maintained

The hashing logic in this tool is reviewed against Apache's published algorithm specifications before each release. If you find a hash that verifies in htpasswd -v but not here, or the reverse, please send us the test case so we can fix it. Last reviewed: February 2026.

Frequently asked questions

What is an .htpasswd file?

An .htpasswd file is a plain-text file where each line stores one user, in the format username:passwordhash. Web servers like Apache and Nginx read this file when a protected URL is requested and compare the credentials the browser sends against what is stored. The plaintext password is never kept in the file, only its hash.

Which hash algorithm should I choose?

Choose bcrypt for any new deployment. It is salted, deliberately slow, and the only format Apache still considers secure today. APR1 MD5 (the $apr1$ prefix) is the portable fallback when bcrypt support is not available, which is common on older Nginx builds where bcrypt is delegated to the system crypt() library. SHA-1 and the old DES crypt format should only be used when interfacing with legacy software that cannot read anything else.

What bcrypt cost should I use?

OWASP recommends a minimum cost of 10 and 12 or higher when latency allows. Each step in cost doubles the work. A cost of 10 runs 1,024 hashing rounds. A cost of 12 runs 4,096 rounds. Cost 14 takes roughly a second per hash on modern hardware. For a website login, cost 12 is a common balance between security and the delay a legitimate user notices.

Where should I put the .htpasswd file?

Always outside your web root. Apache and Nginx do not block access to dotfiles by default. If .htpasswd sits inside /var/www/html, someone who knows the path can download the whole file and run an offline crack against it. Store it in /etc/ or another directory the web server serves nothing from.

What permissions should the file have?

The web server user (www-data on Debian and Ubuntu, apache on RHEL, nginx on some distributions) must be able to read the file. chmod 640 with the file owned by root and grouped under the web server user is the common setup. If the file owner is the same user the web server runs as, 644 is fine. Avoid world-writable permissions.

Does basic auth send the password in plain text?

HTTP Basic Authentication sends the username and password base64-encoded in the Authorization header of every request. Base64 is not encryption. Anyone on the network path can decode it. Basic auth must always be used behind HTTPS. When served over HTTP, the credentials are visible to anyone capturing packets.

Why does Nginx reject my bcrypt hash?

Nginx delegates bcrypt verification to the operating system's crypt() function. On Alpine Linux, which uses musl libc, older builds do not support bcrypt through crypt(). The official nginx:alpine Docker image and recent Debian-based images do support it, but a custom build may not. If bcrypt fails, switch to APR1 MD5, which Nginx implements natively without relying on the system library.

Does this tool send my password anywhere?

No. All hashing runs in your browser using JavaScript. There is no upload, no network request that carries the password, and no server-side logging. You can verify this by loading the page, disconnecting from the internet, and generating a hash — it will still work.

Other developer tools