← News
/PLAYBOOK · 2026-09-05

Nginx Admin Access Control: IP Allowlist and Basic Auth Dual Protection

Public admin panels are the primary target for brute force and exploit. Full Nginx admin access control: allow/deny IP allowlist, auth_basic password auth, satisfy combination logic, and request rate limiting. Includes htpasswd generation and a three-layer verification method for WordPress admin, DB panels, and similar management entry points.

WordPress admin, database panels, monitoring systems—once exposed to the public internet, they become the primary target for brute force and exploit. This article covers full Nginx admin access control: IP allowlist plus Basic Auth, shrinking the admin surface to the minimum. Includes satisfy combination logic, htpasswd generation, and verification.

Applicable Scenarios

Prerequisites

How It Works

Nginx provides two access-control directives: allow / deny match on IP at the network layer, top-down, first hit wins; auth_basic requires HTTP Basic credentials. Credentials travel Base64-encoded, so they must be used with HTTPS.

Combination logic is controlled by satisfy: `satisfy all` (default) requires both IP allowlist and password—fits admins whose IP may change; a new egress IP can still log in with the password. `satisfy any` grants access if either condition passes. That enables passwordless entry from company IPs. The reverse requirement—everyone else blocked even with a password—is usually implemented as allowlist pass-through, remaining IPs deny all. Production recommendation: satisfy all dual check, defense in depth.

Procedure

Step 1: Generate the password file

# Method 1: htpasswd
yum install -y httpd-tools        # Debian/Ubuntu: apt install apache2-utils
htpasswd -cB /etc/nginx/.htpasswd admin   # -c first create, -B bcrypt

# Method 2: openssl (no httpd-tools)
printf "admin:$(openssl passwd -apr1 'YourStrongPwd!')" > /etc/nginx/.htpasswd

chmod 640 /etc/nginx/.htpasswd
chown root:nginx /etc/nginx/.htpasswd

-B forces bcrypt, stronger against brute force than default MD5 crypt. Keep the password file outside the web root so it cannot be downloaded.

Step 2: Configure location access control

server {
    listen 443 ssl;
    server_name example.com;

    # WordPress admin
    location ^~ /wp-login.php {
        allow 203.0.113.10;        # office egress IP
        allow 198.51.100.0/24;     # branch subnet
        allow 127.0.0.1;
        deny all;

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

        include enable-php.conf;   # PHP proxy config for the actual environment
    }

    # DB admin panel: allowlist only, no password
    location ^~ /phpmyadmin/ {
        allow 203.0.113.10;
        deny all;
        root /data/www;
    }
}

Key points: location `^~` prefix match outranks regex, so this control is not stolen by another regex location. wp-login.php is a standalone file and needs its own location; protecting only `/wp-admin/` misses the login entry. `deny all` is required; addresses that miss every `allow` are not rejected without it.

Step 3: Force HTTPS and limit attempt rate

Basic Auth credentials are Base64 plaintext. Site-wide HTTPS is a prerequisite. Rate-limit 401/403 responses to cut brute-force efficiency:

http {
    limit_req_zone $binary_remote_addr zone=adminlimit:10m rate=5r/m;
}

server {
    location ^~ /wp-login.php {
        limit_req zone=adminlimit burst=3 nodelay;
        # ... allow/deny and auth_basic config from above
    }
}

rate=5r/m means the same IP gets at most 5 requests per minute. Enough for a real admin; scripted brute force largely fails.

Verification

nginx -t && nginx -s reload

# Allowlist IP + correct password: expect 200
curl -I -u admin:YourStrongPwd! https://example.com/wp-login.php

# IP outside allowlist: expect 403, and no password prompt
curl -I https://example.com/wp-login.php

# Allowlist IP + wrong password: expect 401
curl -I -u admin:wrongpwd https://example.com/wp-login.php

Results of 200 / 403 / 401 respectively mean satisfy all logic is in effect. Then confirm in a browser that the auth dialog appears, and run one full admin workflow to verify no functional impact.

FAQ

FAQ 1: After config, admin login redirect loops, or uploads return 413?

Redirect loops usually mean the PHP location was not included, so the request is served as static. 413 is the default 1M `client_max_body_size`; add `client_max_body_size 64m;` in that location or the http block. If the auth_basic location includes fastcgi, copy the full PHP handling config; otherwise even allowlisted requests cannot execute PHP.

FAQ 2: Admin home broadband IP changes often. What then?

Three options, in priority order: a dynamically resolving hostname with `resolver` and variable allow (needs GeoDNS/script updates); keep auth_basic only and add fail2ban on 401 to ban brute-force IPs; use a VPN with a fixed egress. Do not open large ISP ranges for convenience—that abandons the IP layer.

Summary

Admin panels are the attacker's first target. Nginx allow/deny plus auth_basic compresses the exposure surface by an order of magnitude in a dozen lines. Deploy satisfy all dual check, site-wide HTTPS, and request rate limiting as a three-layer stack. Recheck allowlist IPs on a schedule; update immediately on employee departure or office-network moves so the policy does not rot.