Configuring Nginx Rate Limiting with Cloudflare Proxy

When using Cloudflare as a reverse proxy for your website, you may notice that your server only logs Cloudflare’s IP addresses rather than those of individual users. This happens because Cloudflare handles incoming requests on your behalf, shielding your origin server from direct user interaction and, in the process, masking the users’ actual IP addresses.

example diagram of different clients connecting directly to the server, logging their true ip, different clients connecting with a reverse proxy to the server, logging the ip of the reverse proxy, different clients connecting with a reverse proxy to the modded server that logs their true ip

If you’re troubleshooting or implementing rate limiting, logging only Cloudflare’s IPs can cause challenges. For example, let’s test the connection and inspect the logs:

root@server:/# for i in {1..10}; do curl -I -s "https://<website>" | head -n 1; done
HTTP/2 200 
HTTP/2 200 
HTTP/2 200 
HTTP/2 200 
HTTP/2 200 
HTTP/2 200 
HTTP/2 200 
HTTP/2 200 
HTTP/2 200 
HTTP/2 200 

Then, by checking the NGINX access logs, we observe:

root@server:/# tail /var/log/nginx/access.log
172.69.225.167 - - [10/Nov/2024:12:17:54 +0100] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.88.1"
162.158.130.21 - - [10/Nov/2024:12:17:55 +0100] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.88.1"
188.114.102.81 - - [10/Nov/2024:12:17:55 +0100] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.88.1"
162.158.129.67 - - [10/Nov/2024:12:17:55 +0100] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.88.1"
172.69.225.145 - - [10/Nov/2024:12:17:55 +0100] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.88.1"
188.114.102.59 - - [10/Nov/2024:12:17:55 +0100] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.88.1"
188.114.102.195 - - [10/Nov/2024:12:17:56 +0100] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.88.1"
172.69.225.167 - - [10/Nov/2024:12:17:56 +0100] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.88.1"
188.114.102.206 - - [10/Nov/2024:12:17:56 +0100] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.88.1"

Each request has a unique IP, even though we’re the only ones sending them. This variation can cause rate limiting rules to misfire since Cloudflare’s IPs rotate across connections. To ensure the server uses the original client IP for logging and rate limiting, we’ll need to configure NGINX to recognize these forwarded IPs.

Configuring NGINX for Original Client IP Detection

To achieve this, edit the main nginx.conf file. In the http section, add the following settings to recognize Cloudflare’s proxy IP ranges:

# https://www.cloudflare.com/ips/
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 199.27.128.0/21;
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;

Each line here specifies an IP range from Cloudflare, ensuring NGINX knows these IPs are trusted and should look for the true client IP in the headers.

Next, add the following directive:

real_ip_header CF-Connecting-IP;

This line tells NGINX to use the CF-Connecting-IP header, which Cloudflare automatically adds to each request, to track the client’s actual IP. As Cloudflare occasionally updates its IP ranges, remember to revisit their IP list periodically.

Updating NGINX Site Configurations

Within your site’s NGINX configuration, update the headers that handle the client’s IP:

proxy_set_header X-Real-IP $http_cf_connecting_ip;
proxy_set_header X-Forwarded-For $http_x_forwarded_for;
# Old headers
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

Enable Rate Limiting in NGINX

For rate limiting, configure a rule using Cloudflare’s CF-Connecting-IP to track each client individually:

limit_req_zone $http_cf_connecting_ip zone=ip:10m rate=10r/s;

In the server block for your website, apply the limit_req directive to enforce rate limiting:

limit_req_zone $http_cf_connecting_ip zone=ip:10m rate=10r/s;

      server {
        server_name <website>;

        location / {
            limit_req zone=ip burst=12 nodelay;
            ...
        }
}

This configuration now limits each unique client to 10 requests per second, preventing abuse without misidentifying each request as unique.