Nginx Basic Auth: Quick Access Control Without a Full Auth System

Sometimes you need to protect a URL quickly — a staging environment, an admin panel, a Prometheus metrics endpoint, internal documentation. Setting up a full authentication system is overkill. Nginx’s built-in basic authentication handles these cases well.

Basic auth has a reputation for being insecure, but that reputation is mostly from HTTP days. Over HTTPS, basic auth is perfectly fine for protecting resources that don’t need fine-grained access control. The credentials are base64-encoded, not encrypted — but HTTPS wraps the whole request, so the credentials are never exposed in transit.

Creating the Password File

Basic auth uses an htpasswd file — a text file where each line is username:hashed_password.

Install the tools if needed:

apt install apache2-utils    # Ubuntu/Debian
# or
yum install httpd-tools      # CentOS/RHEL

Create a new file with the first user:

htpasswd -c /etc/nginx/.htpasswd alice

-c creates the file. You’ll be prompted for a password. Add more users (without -c, which would overwrite the file):

htpasswd /etc/nginx/.htpasswd bob
htpasswd /etc/nginx/.htpasswd carol

View the file to confirm:

cat /etc/nginx/.htpasswd

Each line looks like alice:$apr1$xyz... — the password is stored as a hash, not plaintext. Bcrypt (-B flag) is supported too and is more secure:

htpasswd -B /etc/nginx/.htpasswd dave

Set appropriate permissions:

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

Enabling Basic Auth in Nginx

server {
    listen 443 ssl;
    server_name staging.example.com;

    ssl_certificate /etc/ssl/certs/staging.pem;
    ssl_certificate_key /etc/ssl/private/staging.key;

    # Protect the entire site
    auth_basic "Staging Area";
    auth_basic_user_file /etc/nginx/.htpasswd;

    location / {
        proxy_pass http://127.0.0.1:3000;
    }
}

auth_basic "Staging Area" — the realm string shown in the browser’s password prompt. Set it to something descriptive.
auth_basic_user_file — path to the htpasswd file.

Test and reload:

nginx -t
systemctl reload nginx

Protecting a Specific Location

Often you want to protect just one path rather than the whole site:

server {
    listen 443 ssl;
    server_name example.com;

    # Rest of site is public
    location / {
        root /var/www/html;
    }

    # Protect the admin area
    location /admin/ {
        auth_basic "Admin Area";
        auth_basic_user_file /etc/nginx/.htpasswd;
        proxy_pass http://127.0.0.1:3000;
    }

    # Protect Prometheus metrics
    location /metrics {
        auth_basic "Metrics";
        auth_basic_user_file /etc/nginx/.htpasswd;
        proxy_pass http://127.0.0.1:9090/metrics;
    }
}

IP Allowlist Combined with Basic Auth

For extra security, combine IP restrictions with basic auth:

location /admin/ {
    # Allow internal network without auth
    satisfy any;
    allow 10.0.0.0/8;
    allow 192.168.0.0/16;
    deny all;

    auth_basic "Admin Area";
    auth_basic_user_file /etc/nginx/.htpasswd;

    proxy_pass http://127.0.0.1:3000;
}

satisfy any means access is granted if either the IP is in the allow list OR valid credentials are provided.

satisfy all (the default) would require both — correct IP AND valid credentials.

Use satisfy all for highly sensitive areas, satisfy any for convenience where you want internal users to skip the password prompt.

Disabling Basic Auth for Specific Sublocations

If basic auth is set on a parent location but you need to exclude a sublocation:

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

    location /public/ {
        auth_basic off;
        root /var/www;
    }
}

auth_basic off disables it for that location block. Useful for health check endpoints that need to be publicly accessible within a protected app.

Managing Users

Remove a user:

htpasswd -D /etc/nginx/.htpasswd alice

Change a password:

htpasswd /etc/nginx/.htpasswd alice

Verify a password works (doesn’t actually do this directly, but you can check the hash format is valid):

cat /etc/nginx/.htpasswd

After any changes to the htpasswd file, you don’t need to reload Nginx — it reads the file on each request.

Passing Auth to Backend

If your backend application needs to know who’s authenticated, pass the username via a header:

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

    proxy_pass http://127.0.0.1:3000;
    proxy_set_header X-Remote-User $remote_user;
}

$remote_user is set by Nginx after successful basic auth authentication. Your backend can read the X-Remote-User header to know which user is making the request.

A Note on Security

Basic auth over HTTPS is secure in transit but has some downsides:

  • Credentials are sent with every request (no sessions or tokens)
  • Brute-force protection requires additional tooling (Fail2ban can monitor 401 responses)
  • Browser caches credentials — logging out requires closing the browser or clearing credentials

For developer access, internal tools, and staging environments, these tradeoffs are usually acceptable. For anything more sensitive — user accounts, financial data — you want a proper auth system.

Also consider the password quality and rotation policy. Simple htpasswd files don’t enforce any password policy. I use strong passwords (generated, not memorable) and rotate them whenever someone leaves the team.

Rate limiting the auth endpoints is also worth doing to prevent brute-force:

limit_req_zone $binary_remote_addr zone=auth:10m rate=5r/m;

location /admin/ {
    limit_req zone=auth burst=5 nodelay;
    auth_basic "Admin";
    auth_basic_user_file /etc/nginx/.htpasswd;
    proxy_pass http://127.0.0.1:3000;
}

5 requests per minute per IP stops brute-force attempts without affecting normal use. For more on rate limiting, Nginx rate limiting configuration goes into the full details of setting up limits effectively.

Scroll to Top