A .htpasswd line, built in your browser
Basic auth for Apache and nginx reads a file of username and hash pairs, one per line. This builds that line, in any of the four formats the two servers accept. The bcrypt option is the same algorithm as the rest of this site, written with the $2y prefix that Apache expects.
Everything here runs locally
The strongest option, and the one Apache documents as its bcrypt format. Apache reads it from 2.4.4 onward on every platform. nginx passes the hash to the system crypt(), which knows bcrypt on libxcrypt (Debian 11+, Ubuntu 20.04+, RHEL 8+, Fedora 30+) and on musl, so on any current Linux it works.
Wiring it up
Save the line into a file the server can read, then point the server at it. Put the file somewhere the web root does not reach, otherwise anyone can download your hashes.
Apache
<Directory "/var/www/private">
AuthType Basic
AuthName "Restricted"
AuthUserFile /etc/apache2/.htpasswd
Require valid-user
</Directory>nginx
location /private/ {
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
}The four formats, and what they cost you
- bcrypt
- The strongest option, and the one Apache documents as its bcrypt format. Apache reads it from 2.4.4 onward on every platform. nginx passes the hash to the system crypt(), which knows bcrypt on libxcrypt (Debian 11+, Ubuntu 20.04+, RHEL 8+, Fedora 30+) and on musl, so on any current Linux it works.
- SHA-512 crypt
- The fallback when bcrypt is not available. Apache writes it with htpasswd -5 and calls it secure; nginx gets it from glibc, which has had it since 2007, so it works on every Linux including the ones whose crypt() has no bcrypt. Not on Windows, and weaker than bcrypt at the default round count.
- apr1 (MD5)
- Apache’s own MD5 variant. Weak by modern standards, but it is the one format that works everywhere, including nginx on any platform.
- SHA-1
- Unsalted and fast, which makes it the worst of the three for passwords. Present only because some older setups still expect it.
The apr1 format is Apache's own variant of md5crypt: a thousand MD5 rounds over the password and salt. That was a reasonable amount of work in 1996 and is not one now, so treat it as compatibility rather than protection, and use bcrypt where you can.
SHA-512 crypt sits between the two. It runs five thousand SHA-512 rounds by default, which you can raise, and it is the only format here besides apr1 that a stock glibc will read. What it does not give you is bcrypt's resistance to being run in parallel on a graphics card, so more rounds buy less than the same effort spent on bcrypt would. Reach for it when bcrypt is not on the table, not instead of it.
Doing it on the command line instead
If you have Apache's tools installed, htpasswd does the same job. The -B flag selects bcrypt and -C sets the cost; -5 selects SHA-512 crypt and -r sets its rounds.
# create the file and add the first user
htpasswd -cB -C 12 /etc/apache2/.htpasswd deploy
# add another user to an existing file
htpasswd -B -C 12 /etc/apache2/.htpasswd ci
# print a line without touching any file
htpasswd -nbB -C 12 deploy 'the password'
# SHA-512 crypt instead, for a server that cannot read bcrypt
htpasswd -nb5 -r 100000 deploy 'the password'Watch the -c flag. It creates a new file and will overwrite an existing one along with every user already in it.