Using nginx to detect the Real Client IP and return a Status 403 Forbidden when your Site is Proxied by Cloudflare

by James McDonald | Jan 29, 2025 | IT Tips | 0 comments

When using Cloudflare proxying. By default the nginx logs show the remote_addr (162.158.3.22) as one of the multitudinous Cloudflare proxy server IP addresses:

Following is how you can configure your nginx conf files to return 403 (Forbidden) to specified IP's and Subnets based on the CF-Connecting-IP header that Cloudflare sets with each proxied request

Check your instance of nginx has the geo module

Create a geo block conf file

# /etc/nginx/geo-block.conf
geo $http_cf_connecting_ip $block {
        default         0;
        # list your IPv4 and IPv6 IPs and subnets you want to block here followed by a 1
        154.26.130.54   1;
        188.166.220.242 1;
        1.14.77.81  1; 
        134.199.144.0/20    1;
        2403:3433:21f:2ad0::/64 1;
        2403:3433:21f:2ad0:234::1 1;
}

Matched CF-Connecting-IP header IPs or Subnets set $block to be 1 (true), or 0 (false) by default

If you want to block all by default set default 1; and then add IPs and networks as with a 0 e.g. 10.11.0.0/16 0;

In the http context of your nginx config create a new custom log format that includes $http_cf_connecting_ip

# /etc/nginx/nginx.conf
http {
        # Custom logging Settings
        log_format cloudflareformat '$http_cf_connecting_ip $remote_addr $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent"';

      # remember to include the geo block conf
      include geo-block.conf

Reference the new log format of the site that is proxied by Cloudflare

server {
    server_name example.com.au www.example.com.au;
    access_log /var/log/nginx/tgn_access.log cloudflareformat;
    # lots of other config stuff...

Put in a some logic to return 403 when the connecting IP is matched

In the server context of an nginx site e.g. /etc/nginx/sites-enabled/default.conf add an if statement to return 403 if geo sets $block to 1 (true)

server {
     server_name example.com.au www.example.com.au;
     access_log /var/log/nginx/tgn_access.log cloudflareformat;
     // I put this after server_name before 
     if ($block) {
                return 403;
     }

Finally each time you edit the geo-block.conf file to include some new IP addresses to deny.

Check the syntax and run a reload

nginx -t

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

# finally get nginx to reread its conf files if they have changed
systemctl reload nginx.service

Example of tailed logs showing an automated attack and a 403 Access denied code being returned based on the value of $http_cf_connecting_ip

If the attacking client respects 403's it stops sending requests

If not it will keep sending requests, which are denied, until the remote end gives up

As seen from browser

More information on the options available for configuring your webserver to sit behind Cloudflares proxy service:

https://developers.cloudflare.com/support/troubleshooting/restoring-visitor-ips/restoring-original-visitor-ips

0 Comments

Submit a Comment

Your email address will not be published. Required fields are marked *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.