HTTP basic authentication makes the browser ask for a username and password before showing a page. It’s not a replacement for an application’s own login, but it’s a quick, effective extra layer for staging sites, admin tools, monitoring dashboards and private files.

1. Create a password file

Install the htpasswd tool:

sudo apt install -y apache2-utils        # Debian/Ubuntu
sudo dnf install -y httpd-tools          # RHEL family

Create the file with a first user (-c creates it — only use it the first time, or you’ll overwrite existing users):

sudo htpasswd -c -B /etc/nginx/.htpasswd alice
sudo htpasswd -B /etc/nginx/.htpasswd bob       # add more users

-B uses bcrypt hashing. Without htpasswd, OpenSSL can generate entries (with a weaker hash):

printf "alice:$(openssl passwd -apr1)\n" | sudo tee -a /etc/nginx/.htpasswd

Protect the file:

sudo chown root:www-data /etc/nginx/.htpasswd
sudo chmod 640 /etc/nginx/.htpasswd

Keep it outside the web root, so it can never be downloaded.

2. Protect a whole site

server {
    server_name staging.example.com;
    auth_basic "Staging";
    auth_basic_user_file /etc/nginx/.htpasswd;
    # …
}

The string after auth_basic is the “realm” shown in some browser prompts.

3. Protect one path

location /admin/ {
    auth_basic "Admin";
    auth_basic_user_file /etc/nginx/.htpasswd;
    try_files $uri $uri/ /index.php?$args;
}

For a PHP admin area, the PHP handling must also be protected — otherwise /admin/index.php can be requested directly and matched by the general \.php$ location. Put protection at server level, use a nested PHP location inside the protected one, or use ^~ so regex locations don’t win:

location ^~ /admin/ {
    auth_basic "Admin";
    auth_basic_user_file /etc/nginx/.htpasswd;

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }
}

See Nginx location blocks for why this matters.

4. Exclude a path from protection

Inside a protected server, turn it off where needed — for example Let’s Encrypt validation or a health check:

location /.well-known/acme-challenge/ {
    auth_basic off;
}
location = /health {
    auth_basic off;
    return 200 "ok";
}

5. Combine with IP restrictions

Allow your office without a password, require a password for everyone else:

location /admin/ {
    satisfy any;
    allow 203.0.113.0/24;
    deny all;
    auth_basic "Admin";
    auth_basic_user_file /etc/nginx/.htpasswd;
}

satisfy any = IP or password; satisfy all (default) = IP and password.

6. Apply and test

sudo nginx -t && sudo systemctl reload nginx
curl -I https://staging.example.com/                    # 401 Unauthorized
curl -I -u alice:password https://staging.example.com/  # 200

Security notes

  • Only over HTTPS. Basic auth sends credentials base64-encoded (not encrypted) with every request. Without TLS anyone on the path can read them. See Let’s Encrypt with Nginx.
  • Brute force: add rate limiting to protected locations, or Fail2Ban’s nginx-http-auth jail, which bans IPs with repeated failures.
  • No logout: browsers cache credentials until closed. Fine for admin tools; not for user-facing logins.
  • Shared accounts: give each person their own entry so you can revoke access individually (htpasswd -D /etc/nginx/.htpasswd bob).

When to use something stronger

For several internal apps, consider single sign-on with 2FA in front of everything — Authelia or Authentik integrate with Nginx via auth_request. Or keep admin tools off the internet entirely behind a VPN like WireGuard.

Related: Nginx server blocks and WordPress staging sites.

  • Nginx
  • Security
  • Authentication