HTTP Basic Auth lets you password-protect a directory, a service page or an entire staging copy of a site without changing a single line of application code. The browser shows the login window by itself. Below: how to create a password file, configure Nginx and Apache, protect the WordPress login page and what the limits of this protection are.
When Basic Auth fits
It is a built-in web server mechanism: it checks the login and password before the request reaches PHP or another application. It works well in these cases:
- hiding a staging copy of a site from search robots and accidental visitors;
- hiding service pages: a database panel, statistics, the admin login page;
- adding a second barrier in front of an application login form.
It is not suitable for public pages with customer registration: there is no logout, no password recovery and no lockout against guessing.
Creating the password file
The file is created by the htpasswd utility. It ships in the apache2-utils package on Debian and Ubuntu and in httpd-tools on the RHEL family. The -c option creates the file and overwrites an existing one, so do not use it for the second and later users. The -B option selects bcrypt. Do not pass the password on the command line with -b: it would end up in the shell history.
sudo apt install apache2-utils
sudo dnf install httpd-tools
sudo htpasswd -B -c /etc/nginx/.htpasswd admin
sudo htpasswd -B /etc/nginx/.htpasswd editor
The file must not live in the site directory. Allow reading only to the owner and to the group the web server runs as (for example www-data on Debian and Ubuntu), and test the login by hand: if the server does not accept the chosen algorithm, the login will fail.
Configuration in Nginx
Add two directives to the server block or to the needed location. Everything under the /admin/ prefix will require a password.
location /admin/ {
auth_basic "Restricted area";
auth_basic_user_file /etc/nginx/.htpasswd;
}
The WordPress login page is protected with an exact match. The authorization directives must go into the block that passes the request to PHP, otherwise the page opens without a password.
location = /wp-login.php {
auth_basic "Restricted area";
auth_basic_user_file /etc/nginx/.htpasswd;
# ... existing PHP directives
}
Check the configuration and reload it. An exception for a single path (for example, for receiving notifications from a payment system) is made with the auth_basic off directive in its location.
sudo nginx -t
sudo systemctl reload nginx
Configuration in Apache
In Apache the rules go into the .htaccess file of the needed directory, provided the configuration allows AllowOverride AuthConfig. Working with this file is also covered in the article Redirects with .htaccess.
AuthType Basic
AuthName "Restricted area"
AuthUserFile /home/user/.htpasswd
Require valid-user
The path in AuthUserFile must be absolute. To protect a single file instead of a directory, use a Files block.
<Files "wp-login.php">
AuthType Basic
AuthName "Restricted area"
AuthUserFile /home/user/.htpasswd
Require valid-user
</Files>
Limitations and protection against guessing
Basic Auth is simpler than any form-based login, and it has weak points. Before relying on it, keep the following in mind. Issuing a certificate is described in the article Nginx and Certbot.
- The login and password are encoded but not encrypted, and they travel with every request. Without HTTPS anyone listening to the channel will see them.
- There is no lockout: passwords can be guessed as many times as you like.
- If you protect an entire WordPress site, REST API requests with application passwords stop working, because they use the Authorization header too. Protect individual paths.
Against guessing, install fail2ban: it can read the web server error log and ban addresses after failed logins. The setup is described in the article Setting up fail2ban, and general Nginx measures are collected in the article Nginx security hardening.
Frequently asked questions
How do I password-protect a site in Nginx?
Create a password file with htpasswd, then add the auth_basic directive with a prompt text and auth_basic_user_file with the file path to the needed server or location. Check the configuration with nginx -t and reload it. After that the browser will ask for a login and password before showing pages.
How do I password-protect a WordPress site?
The simplest way is to protect only the wp-login.php login page and the wp-admin directory rather than the whole site. Visitors and the REST API keep working that way. For a staging copy it is fine to protect everything. Remember that without HTTPS the password travels in the clear, so install a certificate first.
Is Basic Auth safe without HTTPS?
No. The login and password travel in every request encoded with base64, which is not encryption. Anyone who intercepts the traffic reads them in a second. Use Basic Auth only over HTTPS and add lockout against guessing through fail2ban, otherwise the password can be guessed endlessly.
Why does the browser not ask for a password after my change?
Most often the Nginx configuration was not reloaded, Apache does not allow AllowOverride AuthConfig, or the directives sit in the wrong block: another location handles the request, for example the one that passes it to PHP. Check which block really serves the address and reload the web server configuration.