We may earn a referral fee at no additional cost to you when you shop through the links on this page.
A quick password lock on a staging site, admin path, or private dashboard often does not need a full login system. nginx has it built in through HTTP Basic Authentication, and this DevAnswers guide installs the password tool, creates a credentials file, switches on auth_basic, and verifies it with a 401-then-200 test.
Prerequisites
Before you start, make sure you have:
- nginx already installed and serving a site
- A shell with
sudoor root access - A
serverblock (or a specificlocation) you want to protect
Install the Password File Tool
nginx does the authentication itself through its built-in auth_basic module, so there is no separate auth server to install. All it needs is a small file of usernames and hashed passwords to check against. The usual tool to generate that file is htpasswd, which on Debian and Ubuntu ships in the apache2-utils package; despite the name, you are installing a tiny helper, not Apache itself:
sudo apt update
sudo apt install apache2-utils
You do not actually need Apache for this; the package just bundles the htpasswd tool. If you would rather not install it at all, there is a pure-openssl alternative further down.
Create the Password File
Create the file with htpasswd. The -c flag creates a new file, so use it only the first time. Store the file outside your web root (under /etc/nginx/ is conventional) so it can never be served as a download:
sudo htpasswd -c /etc/nginx/.htpasswd billy
New password:
Re-type new password:
Adding password for user billy
htpasswd -c overwrites the file if it already exists, wiping every account in it. Use -c only when creating the file the first time; to add more users, run htpasswd without it. To add a second user, drop the -c so the existing entries are kept:
sudo htpasswd /etc/nginx/.htpasswd ciara
Pick a strong, random secret for each account, since this is your only barrier; a password generator is handy here. Each line in the file is just username:hashed-password. nginx accepts several hash formats, including the Apache APR1 hash that both htpasswd and openssl‘s -apr1 flag produce, so if you skipped apache2-utils you can append an entry with openssl instead:
# Only if you did NOT install apache2-utils above
echo "billy:$(openssl passwd -apr1)" | sudo tee -a /etc/nginx/.htpasswd
Turn On Basic Auth in Nginx
nginx’s built-in ngx_http_auth_basic_module does the checking. In the server or location block you want to protect, add two directives: auth_basic with a realm name (shown in the browser prompt), and auth_basic_user_file pointing at the file you just made. Add this inside your site’s server block:
location /admin/ {
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;
}
That protects just the /admin/ path. To lock the entire site instead, put the same two directives in the server block rather than a location block, and every request will need credentials. You can also carve out an exception: drop auth_basic off; into a nested location (for example a public /admin/status health check) to lift the prompt for that sub-path only.
The password file only needs to be readable by the nginx worker process, not world-writable, so the defaults under /etc/nginx/ are fine. Test the configuration and reload nginx to apply it:
sudo nginx -t && sudo systemctl reload nginx
nginx: configuration file /etc/nginx/nginx.conf test is successful
Test It: 401 Then 200
Confirm the lock works with a quick local check from the command line (plain http is fine on the box itself; a public site must be served over HTTPS, as the summary explains). A request with no credentials should be refused with 401 Unauthorized, because nginx returns that status (with a WWW-Authenticate header) whenever the credentials are missing or do not match the file:
curl -I http://devanswers.test/admin/
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="Restricted Area"
Now send a valid username and password with -u. Correct credentials are required to get in, so this returns 200 OK:
curl -I -u billy:your-password http://devanswers.test/admin/
HTTP/1.1 200 OK
In a browser, that same 401 is what triggers the native username/password dialog, with your realm name (“Restricted Area”) shown as the label. If you see a 500 instead, nginx usually cannot read the password file: check the path and permissions in the nginx error log. And because browsers cache Basic Auth credentials for the session, close the tab (or use a private window) when you want to re-test the prompt from scratch.
Let Trusted IPs Skip the Prompt
You can combine the password with an IP allowlist so your own office or VPN is let straight through while everyone else is challenged. nginx controls this with the satisfy directive: set it to any and access is granted if either the address matches or the password is correct.
location /admin/ {
satisfy any;
allow 203.0.113.10; # your trusted IP
deny all;
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;
}
Requests from 203.0.113.10 now skip the password box; every other address still has to authenticate. This is handy for letting an uptime monitor, a CI runner, or your office VPN through by address while human visitors elsewhere still get the prompt. Use satisfy all instead if you want to require both a trusted IP and a password.
Summary
That is the whole flow: install apache2-utils, build a credentials file with htpasswd, point auth_basic and auth_basic_user_file at it, reload, and verify with a 401-then-200 check. One important caveat: Basic Auth sends the username and password base64-encoded, not encrypted, so they are effectively cleartext on the wire. It is only safe over an encrypted connection, so always serve a protected site over HTTPS with a TLS certificate.
To further secure your site against brute-force guessing, you can install and configure Fail2ban to automatically block IPs that repeatedly fail authentication.
Comments