KBeezie

There's no place like ::1

Menu
  • Home
  • Start Here
  • Security Series
  • About

Protecting Folders with Nginx

2026/02/05 in Security

Basic authentication in Nginx is a quick way to gate a directory, an admin area, or an entire staging site behind a username and password. It's not a replacement for a full login system, but for internal tools, dev environments, or adding a second layer in front of something already protected, it does the job with almost no overhead.

When to use it

  • Staging or development sites you don't want indexed or crawled
  • Admin panels (phpMyAdmin, Netdata, etc.) as an extra layer before the application login
  • Internal tools and dashboards with no built-in authentication
  • API endpoints that only trusted clients should reach

Basic auth sends credentials base64-encoded (not encrypted) with every request — so always pair it with SSL. Never use it over plain HTTP.


Step 1: Create a password file

Use openssl to generate a SHA-512 hashed password — stronger than the old DES-based crypt method and built into every Linux system:

# Generate a SHA-512 hash (prompts for password twice)
openssl passwd -6

Paste the output into a password file in the format username:hash, one per line:

admin:$6$saltsalt$SomeReallyLongHashedStringHere
editor:$6$saltsalt$AnotherHashedString

Save this file outside your web root — /etc/nginx/conf/passwords/ is a good home. Give it restrictive permissions:

chmod 600 /etc/nginx/conf/passwords/mysite
chown root:www-data /etc/nginx/conf/passwords/mysite

If you have apache2-utils installed (or don't mind installing it), htpasswd is a convenient alternative:

# Create a new file and add a user
htpasswd -c /etc/nginx/conf/passwords/mysite admin

# Add another user to an existing file (no -c)
htpasswd /etc/nginx/conf/passwords/mysite editor

By default htpasswd uses bcrypt on modern systems, which is fine.


Step 2: Configure Nginx

Add auth_basic and auth_basic_user_file to the location you want to protect:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name staging.mysite.com;

    # ... SSL certificates and root directive ...

    location / {
        # Normal site content
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location /admin {
        auth_basic "Restricted Area";
        auth_basic_user_file /etc/nginx/conf/passwords/mysite;
    }

    # Block hidden files — also protects any stray .htpasswd if left in the web root
    location ~ /\. {
        deny all;
    }
}

auth_basic sets the realm string shown in the browser's password dialog. auth_basic_user_file points to the file you created in Step 1.

Reload Nginx. Visiting /admin now prompts for credentials.


Step 3: Combine with an IP allowlist

You can allow trusted IPs through without a password while requiring authentication from everyone else — useful for your home or office network:

location /admin {
    satisfy any;

    # Trusted IPs — no password required
    allow 203.0.113.50;
    allow 192.168.1.0/24;
    allow 10.0.0.0/8;

    # Everyone else gets the password prompt
    deny all;

    auth_basic "Restricted Area";
    auth_basic_user_file /etc/nginx/conf/passwords/mysite;
}

satisfy any means "pass either check" — if the IP matches, you're in. If it doesn't, you get the auth prompt. Without satisfy any, the default is satisfy all, which would require both a matching IP and a valid password.


Step 4: Rate-limit login attempts

Basic auth has no lockout mechanism — an attacker can hammer credentials indefinitely. Rate limiting slows them to a crawl:

# In nginx.conf (http block)
limit_req_zone $binary_remote_addr zone=authlimit:10m rate=1r/m;

# In your server block
location /admin {
    limit_req zone=authlimit burst=2 nodelay;
    error_page 503 =429 /rate_limit_exceeded;

    auth_basic "Restricted Area";
    auth_basic_user_file /etc/nginx/conf/passwords/mysite;
}

This caps login attempts at one per minute per IP, with a burst of 2. Nginx returns a 503 when the rate limit is exceeded; the error_page directive maps it to HTTP 429 (Too Many Requests) so the client knows what happened.


Step 5: Add fail2ban for repeated failures

Basic auth failures appear in your Nginx error log. fail2ban can watch for them and block offending IPs at the firewall level. Create a filter at /etc/fail2ban/filter.d/nginx-http-auth.conf:

[Definition]
failregex = ^<HOST> - .* "(GET|POST|HEAD).*" 401
ignoreregex =

Enable it in /etc/fail2ban/jail.local:

[nginx-http-auth]
enabled = true
port    = http,https
logpath = /var/log/nginx/*error.log
bantime = 3600
maxretry = 5

After five 401 responses in the bantime window, the IP gets firewall-blocked for an hour.


A note on password file placement

Store your password files outside the web root. If you must keep one inside a web-accessible directory, the location ~ /\. block shown in Step 2 will deny access to any file starting with a dot. But it's safer to keep them entirely elsewhere — /etc/nginx/conf/passwords/ with chmod 600 is the standard approach.

Tags: nginx, basic-auth, htpasswd, fail2ban, hardening, rate-limiting
©2026 KBeezie | Disclaimer | Privacy Notice