How to IP filter a directory with NGINX

In some cases, you may want to make a directory on your website accessible only to approved requesting IPs. A use case for this potentially is “information for your eyes only” - which can contain various sensitive information.

Here is how you can set this up in NGINX for a PHP/WordPress site.

Navigate to the site you want to make the update to in Cleavr. Go to Settings > PHP Configs.

Add the below code beneath the location / statement.

location ^~ /path/to/directory {  
  allow 1.2.3.4;  // IP to whitelist
  try_files $uri $uri/ /index.php?$query_string;
  deny all;
  error_page 403 =404 $scheme://$host/$some_page;
}

Update /path/to/directory to the directory path you want to lock down. Swap out 1.2.3.4. with the IP you wish to whitelist. Also, change out some_page with a location you want to send the user.

Click on Update at the bottom of the page.

Lastly, go to the Server > Services page and click on the NGINX refresh icon to refresh NGINX so that it picks up the changes.

What this does?

This will lock down the directory to only allow access to the designated IP address. If the defined IP is making the request, then they will be granted access to pass through. If not, then error_page 403 =404 $scheme://$host/$some_page; will redirect the user via a 404 response. This way the user does not see the default NGINX 403 error page - which is plane and also tells visitors that there is actually something there that they cannot see.

Things to be aware of

One thing to be aware of is that if your IP is being proxied, then NGINX will pick up the proxied IP instead of your whitelisted IP.

For a real-life example, if you’re using Cloudflare to manage your sites domain AND are proxying the record, then NGINX will see Cloudflare’s IP. This will break the intended behavior.

Cloudflare Proxy

If you are using Cloudflare’s proxy features, then you can work around the proxy IP.

Cloudflare offers suggestions on how to restore original visitor IP and provides their IP ranges to use with NGINX’s real IP module. All you have to do is simply copy and paste into the site’s NGINX settings.

For example, expanding on the previous example, you’d adjust as follows:

set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 104.16.0.0/12;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 131.0.72.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2c0f:f248::/32;
set_real_ip_from 2a06:98c0::/29;
#real_ip_header CF-Connecting-IP;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

location /foo {
  allow 1.2.3.4;
  try_files $uri $uri/ /index.php?$query_string;
  deny all;
  error_page 403 =404 $scheme://$host/$remote_addr;
}

If you need this feature across multiple sites on the same server, you can add a new .conf file in /etc/nginx/conf.d with the above (excluding the location /foo section).

1 Like