How to Allow or Block IP Addresses in Nginx

Allow or block IP addresses in Nginx with allow and deny rules. Covers CIDR ranges, reusable lists, real client IPs, testing, and rollback.

PublishedAuthorJoshua JamesRead time11 minGuide typeNginx

IP-based access rules are useful when an admin path, staging site, partner endpoint, or private dashboard should not be reachable by every visitor that can reach the Nginx listener. The important detail is order: Nginx checks allow and deny rules from top to bottom and stops at the first match.

Use Nginx IP allow and block rules for HTTP request handling, not as a replacement for a firewall, CDN policy, VPN, or application authentication. The safest setup keeps the protected location narrow, confirms the real client IP Nginx sees, tests the configuration with nginx -t, reloads Nginx, and then proves the expected 200 or 403 response.

Understand Nginx IP Access Rules

The official Nginx access module documentation defines the allow and deny directives for limiting requests by client address. Both directives accept a single IP address, a CIDR network, all, and the special unix: value for UNIX-domain socket requests.

DirectiveContextPurpose
allowhttp, server, location, limit_exceptAllows a matching client address, CIDR range, UNIX socket, or all clients.
denyhttp, server, location, limit_exceptDenies a matching client address, CIDR range, UNIX socket, or all clients.
includeanyLoads reusable rule files, as long as the included file contains directives valid in that context.
set_real_ip_fromhttp, server, locationMarks a proxy or load balancer address as trusted for real-client-IP replacement.
real_ip_headerhttp, server, locationNames the header or PROXY protocol source used for the replacement address.
real_ip_recursivehttp, server, locationChooses the last non-trusted forwarded address when recursive lookup is enabled.
geohttpCreates variables from client IP addresses, which helps with larger CIDR lists.

Rule sequence matters more than visual grouping. A broad rule such as deny all; must come after the addresses you want to permit. If deny all; appears first, later allow rules do not rescue the request because Nginx has already found a match.

An allow rule by itself does not block unmatched clients. When no access rule matches, request processing continues, so a real allowlist needs the allowed addresses first and a final deny all;.

location /admin/ {
    allow 203.0.113.10;
    allow 2001:db8:1234::/48;
    deny all;
}

Inheritance is the other common surprise. Access rules from a higher context are inherited only when the current context has no allow or deny directives of its own. As soon as a location defines any access rule, it replaces the inherited access-rule set for that location. Include a final deny all; whenever the location is meant to be a true allowlist.

IP addresses can represent shared offices, VPN exits, mobile carrier gateways, or a CDN edge rather than one person. Use Nginx IP rules as a practical gate for known networks, not as the only authentication control for sensitive applications.

Confirm Your Nginx Baseline

Start with Nginx already installed and serving the site or path you plan to protect. Use the matching distribution guide when the base package setup still needs work:

Find the active include layout before editing a site file. Debian and Ubuntu packages commonly use sites-enabled, while Fedora, RHEL-family, and nginx.org packages commonly load conf.d files. Some Fedora-style server blocks also include default.d snippets inside a selected server.

sudo nginx -T 2>/dev/null | grep -E '^[[:space:]]*include .*(sites-enabled|conf\.d|default\.d)'

Example output can show one or more active include directories, depending on the package source and distribution layout:

include /etc/nginx/default.d/*.conf;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;

If you need a fuller server-block workflow before adding access rules, use the Nginx server blocks and virtual hosts guide first.

Allow or Block IP Addresses in Nginx

Place access rules in the smallest context that owns the decision. Use a location block for an admin path, a server block for a whole hostname, and http only when the same policy should apply broadly across included virtual hosts.

Choose the Nginx Context for IP Rules

Nginx accepts allow and deny in several contexts, but the safest placement is usually the narrowest one that matches the protected surface. Broader contexts are useful when the policy is intentional for every inherited site or path.

ContextUse It ForWatch For
httpA shared rule that should affect all inherited virtual hosts in the same Nginx HTTP configuration.A broad rule can affect unrelated sites loaded from conf.d or sites-enabled.
serverA whole hostname, such as a staging domain, private app, or internal dashboard.Child locations inherit the rule set only until they define their own allow or deny.
locationA direct path rule for one admin area, API route, metrics endpoint, or download folder.Another more specific location can handle the request instead, so confirm location priority when rules appear ignored.
limit_exceptPublic read access with write-style methods limited to trusted addresses.Method restrictions do not replace application authentication for real write permissions.

Apply IP Rules Globally at the HTTP Level

Use the http context only when every server that inherits the block should share the rule. This example blocks one network across the virtual hosts inside the same Nginx HTTP configuration while leaving other clients on the normal request path.

http {
    deny 198.51.100.0/24;

    server {
        listen 80;
        server_name example.com;

        location / {
            try_files $uri $uri/ =404;
        }
    }

    server {
        listen 80;
        server_name app.example.com;

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

Both server blocks inherit the deny rule until a lower server or location context defines its own access-rule set. Avoid a global deny all; unless every inherited hostname is meant to be private.

Block One IP Address

Use a single deny rule when the rest of the location should stay public but one address should be refused. Replace the documentation address with the client IP from your logs.

location / {
    deny 198.51.100.23;

    try_files $uri $uri/ =404;
}

This blocks only 198.51.100.23. Other clients continue through normal file, proxy, or application handling unless another inherited layer blocks them.

Block a CIDR Network

Use CIDR notation when the abusive or restricted traffic comes from a known network range. Put this rule in the server or location where that network should be denied.

server {
    listen 80;
    server_name example.com;

    deny 198.51.100.0/24;

    location / {
        try_files $uri $uri/ =404;
    }
}

A subnet block is a blunt tool. Confirm the range belongs to the traffic you mean to block, especially when logs show cloud providers, VPNs, corporate networks, or mobile carrier gateways.

Write the network address for CIDR ranges, such as 198.51.100.0/24. If you mean one host, use a single IP address or a /32 prefix so nginx -t does not warn that low address bits are meaningless.

Allow One IP Address and Block Everyone Else

An allowlist needs the allowed address first, followed by deny all;. This pattern fits admin panels, staging paths, metrics endpoints, and private dashboards that only trusted networks should reach.

Do not omit the final deny all;. Without that fallback, clients that do not match the allow line continue through normal request handling.

location /admin/ {
    allow 203.0.113.10;
    deny all;

    proxy_pass http://127.0.0.1:3000;
}

The request from 203.0.113.10 continues to the local application. Any other client receives 403 Forbidden before the request reaches the upstream service.

Allow a Subnet with a Denied Exception

Put specific exceptions before broader allowed networks. In this example, the whole partner subnet is allowed except one address.

location /partners/ {
    deny 203.0.113.25;
    allow 203.0.113.0/24;
    deny all;

    proxy_pass http://127.0.0.1:3000;
}

Nginx checks deny 203.0.113.25; before it reaches the broader allow 203.0.113.0/24; rule. Reversing those two lines would allow the exception address because the subnet rule would match first.

Apply IP Rules to a Whole Hostname

Place rules at the server level when the entire virtual host should be private. This is useful for an internal hostname or a staging domain.

server {
    listen 80;
    server_name staging.example.com;

    allow 10.0.0.0/8;
    allow 203.0.113.10;
    deny all;

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

Any location inside this server inherits the rule set unless that location defines its own allow or deny directive. If one child location must be public, make that exception explicit and review it carefully.

Limit Non-GET Methods by IP Address

The limit_except context can restrict methods other than GET and HEAD. This pattern keeps read access public while limiting write-style methods to a trusted network.

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

    limit_except GET {
        allow 203.0.113.10;
        deny all;
    }
}

Allowing GET also allows HEAD, as documented by the Nginx core module. Use application authentication for real write permissions; method-based IP rules are only one request-layer gate.

Use Include Files for Reusable Nginx IP Lists

Reusable include files keep the same allowlist from being copied across several server blocks. The Nginx include directive loads the file in place, so the included file must contain directives that are valid where the file is included.

Create a dedicated allowlist file such as /etc/nginx/allowlists/admin-ips.conf with only the access rules:

sudo install -d -m 0755 /etc/nginx/allowlists
sudo nano /etc/nginx/allowlists/admin-ips.conf

Add only directives that are valid in the place where the file will be included:

# /etc/nginx/allowlists/admin-ips.conf
allow 203.0.113.10;
allow 203.0.113.0/24;
allow 2001:db8:1234::/48;
deny all;

Then include the file inside each protected location:

location /admin/ {
    include /etc/nginx/allowlists/admin-ips.conf;

    proxy_pass http://127.0.0.1:3000;
}

Keep the final deny all; inside the included file only when every location using that file should be private. If one location needs a different fallback decision, use a separate include file instead of relying on memory months later.

Test and Apply Nginx IP Access Changes

Back up the site file before editing a production allowlist or deny rule. Use the file path your Nginx include layout actually loads.

sudo cp -a /etc/nginx/conf.d/example.com.conf /etc/nginx/conf.d/example.com.conf.before-ip-rules

On a Debian-style layout, back up the source file under sites-available instead of the enabled symlink:

sudo cp -a /etc/nginx/sites-available/example.com /etc/nginx/sites-available/example.com.before-ip-rules

Test the full Nginx configuration before reloading. The official Nginx command-line parameter documentation describes -t as the syntax check that also tries to open referenced files.

sudo nginx -t

A successful syntax test prints:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Reload Nginx after the syntax check succeeds:

sudo systemctl reload nginx

On a system without systemd, use Nginx’s reload signal instead:

sudo nginx -s reload

Use curl command examples to test the protected path from the Nginx host. This loopback check proves the origin server block and path, not public DNS or CDN behavior.

curl --noproxy '*' -D - -o /dev/null -H "Host: example.com" http://127.0.0.1/admin/

A blocked request returns 403 Forbidden:

HTTP/1.1 403 Forbidden
Server: nginx

Test from a real allowed address and a real denied address before relying on the rule in production. Ordinary request headers do not change the TCP source IP that Nginx matches unless the real IP module is configured to trust a proxy header.

Do not test access rules with a temporary return 200 in the same protected location. The Nginx rewrite module can return that response before the access module denies the request, so test the real static file, proxy, or application path instead.

When a rule blocks a request, the error log commonly includes the stable phrase access forbidden by rule. Use the Nginx access and error logs guide when you need to match a public request, hostname, URI, and client address.

sudo grep 'access forbidden by rule' /var/log/nginx/error.log | tail -n 5

The matching log line should include the blocked request and the stable phrase access forbidden by rule if the denial came from the access module.

Rollback a Bad IP Access Rule

If a new rule blocks legitimate traffic, restore the backed-up site file, test syntax, and reload. Use the matching path for your layout.

sudo cp -a /etc/nginx/conf.d/example.com.conf.before-ip-rules /etc/nginx/conf.d/example.com.conf
sudo nginx -t && sudo systemctl reload nginx

For a Debian-style site file, restore the file under sites-available:

sudo cp -a /etc/nginx/sites-available/example.com.before-ip-rules /etc/nginx/sites-available/example.com
sudo nginx -t && sudo systemctl reload nginx

If the reusable allowlist file was created only for this change, remove every include /etc/nginx/allowlists/admin-ips.conf; line before deleting the file. Keep the directory when other allowlists still use it.

sudo rm -f /etc/nginx/allowlists/admin-ips.conf
sudo rmdir --ignore-fail-on-non-empty /etc/nginx/allowlists
sudo nginx -t && sudo systemctl reload nginx

Preserve Real Client IPs Behind Proxies

When Nginx sits behind a CDN, load balancer, reverse proxy, Docker network, Kubernetes ingress, or proxy manager, $remote_addr may be the proxy address. An allowlist built against visitor addresses will fail if Nginx only sees the proxy.

Trust only the proxy addresses you control before using X-Forwarded-For or another forwarded header. The official Nginx real IP module documentation covers the directive contexts and the real_ip_recursive behavior.

The real IP module is a build-time module. If nginx -t reports an unknown set_real_ip_from directive, use a package built with that module or put the IP policy at the proxy layer that already sees the client address.

http {
    set_real_ip_from 10.0.0.5;
    set_real_ip_from 2001:db8:10::/48;
    real_ip_header X-Forwarded-For;
    real_ip_recursive on;

    server {
        listen 80;
        server_name example.com;

        location /admin/ {
            allow 203.0.113.10;
            deny all;

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

Do not trust 0.0.0.0/0, ::/0, or arbitrary public networks as real-IP sources. A client can spoof forwarded headers when Nginx accepts those headers from untrusted senders.

Check the address Nginx logs before changing access rules. If the first access-log field is still the CDN or load-balancer address, fix the trusted proxy layer before adjusting the allowlist.

sudo tail -n 20 /var/log/nginx/access.log

For broader proxy headers, upstream routing, and local application backends, use the Nginx reverse proxy configuration guide.

Manage Large Nginx IP Block Lists

Small access lists are fine with plain allow and deny directives. For large CIDR sets, the Nginx access module documentation recommends using geo variables from the Nginx geo module instead of a long sequential rule chain.

A geo block belongs in the http context and can match individual IP addresses or CIDR prefixes. This example returns 403 for listed clients before normal server handling continues.

http {
    geo $blocked_client {
        default 0;
        198.51.100.0/24 1;
        203.0.113.25 1;
        2001:db8:bad::/48 1;
    }

    server {
        listen 80;
        server_name example.com;

        if ($blocked_client) {
            return 403;
        }

        location / {
            try_files $uri $uri/ =404;
        }
    }
}

The if block here performs one narrow action: return a status code. Avoid building complex rewrites or proxy logic into if blocks; use the Nginx if directive guide when a condition needs more than a simple return.

For operational block lists that change often, consider whether a firewall, CDN security rule, load balancer policy, or Fail2Ban workflow owns the problem better than an Nginx config reload. Nginx still needs a reload before normal configuration-file changes take effect.

Troubleshoot Nginx IP Allow and Block Rules

Troubleshooting should identify the request layer before changing rules. Most access-rule failures come from wrong order, wrong context, proxy IP handling, or a different 403 source such as filesystem permissions or application authorization.

SymptomLikely CauseFirst Check
Everyone receives 403 Forbiddendeny all; appears before allowed addresses, or the allowlist is in the wrong location.Dump active allow and deny lines with sudo nginx -T.
A trusted user is blockedNginx sees a proxy, VPN, NAT, or different public address than expected.Compare the access log client address with the allowlist.
A blocked client still reaches the siteThe rule is in a server or location that did not handle the request.Check server-name matching and location priority.
nginx -t failsThe directive is in an unsupported context, the CIDR is invalid, or a semicolon is missing.Read the file and line number printed by the syntax test.
nginx -t warns that low address bits are meaninglessA host address was written with a network prefix, such as 192.168.1.50/24.Use the network address for the CIDR range or use a single-host prefix.
The log says access forbidden by ruleThe access module denied the request as configured.Review rule order and the actual client IP before removing the rule.

Find Active Nginx Access Rules

Use the active configuration dump so included files and symlinked site files are visible in one stream:

sudo nginx -T 2>&1 | awk '/^# configuration file / { file=$0 } /^[[:space:]]*(allow|deny)[[:space:]]/ { if (file != last) { print file; last=file } print }'

Relevant output should show the order Nginx will evaluate:

# configuration file /etc/nginx/conf.d/example.com.conf:
    allow 203.0.113.10;
    deny all;

If deny all; appears before the allowed address in the same context, move the allowed address above it and retest with sudo nginx -t.

Fix Allowed IP Addresses Still Receiving 403

Start with the client address Nginx actually logged, not the address the user expected to have. Shared networks and edge proxies can make those values differ.

sudo grep ' 403 ' /var/log/nginx/access.log | tail -n 20

If the logged address is a CDN or load balancer, configure real IP handling for the trusted proxy or put the allowlist at the edge layer that sees the true client. If the address is correct and the log contains access forbidden by rule, repair the rule order or context.

Fix Block Rules That Do Not Match

A block rule only affects requests that reach the context where the rule lives. If another server block handles the hostname, or a more specific location bypasses the protected location, the rule may never run.

sudo nginx -T 2>&1 | awk '/^# configuration file / { file=$0 } /^[[:space:]]*(server_name|location|allow|deny)[[:space:]]/ { if (file != last) { print file; last=file } print }'

Check the matching hostname first, then check the selected location. When path matching is the issue, compare the behavior with Nginx location block priority before moving rules between locations.

Fix Syntax Errors in IP Access Rules

Nginx reports parser errors with a file and line number. A missing semicolon, invalid CIDR prefix, or access directive inside the wrong block prevents reload.

sudo nginx -t
nginx: [emerg] "allow" directive is not allowed here in /etc/nginx/conf.d/example.conf:12
nginx: configuration file /etc/nginx/nginx.conf test failed

Move allow and deny into an http, server, location, or limit_except context. Do not place access directives inside upstream, map, or arbitrary if blocks.

If nginx -t succeeds but prints low address bits are meaningless, the prefix still loaded but probably does not express the range you intended. Replace a value such as 192.168.1.50/24 with 192.168.1.0/24 for the whole subnet, or with 192.168.1.50/32 for one IPv4 host.

Separate Access Rules from Other 403 Causes

Not every 403 Forbidden response comes from allow and deny. Missing index files, file permissions, SELinux labels, dotfile rules, upstream applications, and CDN policies can all return 403. If the error log does not contain access forbidden by rule, use the Nginx 403 Forbidden troubleshooting guide to identify the real layer.

Conclusion

Nginx IP access control is ready when the protected context is narrow, allowed addresses appear before broad denials, proxy headers are trusted only from controlled edges, and the same path returns the expected 200 or 403 after a clean syntax test and reload. For adjacent hardening, combine these rules with Nginx rate limiting where repeated requests need throttling instead of a permanent block.

Share this guide

Help another Linux user troubleshoot faster

Share this guide with someone troubleshooting Linux systems or saving it for later.

Follow LinuxCapable

Want more LinuxCapable guides in Google?

Add LinuxCapable as a preferred source so Google can show more of our fresh Linux tutorials in Top Stories and From your sources when relevant.

Add LinuxCapable as a preferred source on Google
Search LinuxCapable

Need another guide?

Search LinuxCapable for package installs, commands, troubleshooting, and follow-up guides related to what you just read.

Found this guide useful?

Support LinuxCapable to keep tutorials free and up to date.

Buy me a coffeeBuy me a coffee
Before commenting, please review our Comments Policy.
Formatting tips for your comment

You can use basic HTML to format your comment. Useful tags currently allowed in published comments:

You type Result
<code>command</code> command
<strong>bold</strong> bold
<em>italic</em> italic
<blockquote>quote</blockquote> quote block

Got a Question or Feedback?

We read and reply to every comment - let us know how we can help or improve this guide.

Verify before posting: