If your HTTP server is running behind Cloudflare, it is recommended to only allow traffic from Cloudflare IP addresses. We can configure this systematically using iptables
, as demonstrated in Allowing Cloudflare IP addresses.
However, if you have some other services not proxied by Cloudflare, it’s also flexible to white list Cloudflare IPs just inside the Nginx service.
To do this, create a /etc/nginx/allow-cloudflare-only.conf
configuration file that allows all of Cloudflare IPs:
# https://www.cloudflare.com/ips
# IPv4
allow 173.245.48.0/20;
allow 103.21.244.0/22;
allow 103.22.200.0/22;
allow 103.31.4.0/22;
allow 141.101.64.0/18;
allow 108.162.192.0/18;
allow 190.93.240.0/20;
allow 188.114.96.0/20;
allow 197.234.240.0/22;
allow 198.41.128.0/17;
allow 162.158.0.0/15;
allow 104.16.0.0/13;
allow 104.24.0.0/14;
allow 172.64.0.0/13;
allow 131.0.72.0/22;
# IPv6
allow 2400:cb00::/32;
allow 2606:4700::/32;
allow 2803:f800::/32;
allow 2405:b500::/32;
allow 2405:8100::/32;
allow 2a06:98c0::/29;
allow 2c0f:f248::/32;
deny all; # deny all remaining ips
# Generated at Wed Oct 30 17:44:50 UTC 2024
This configuration file can be easily generated by this script:
#!/usr/bin/bash
set -e
cf_ips() {
echo "# https://www.cloudflare.com/ips"
for type in v4 v6; do
echo "# IP$type"
curl -sL "https://www.cloudflare.com/ips-$type/" | sed "s|^|allow |g" | sed "s|\$|;|g"
echo
done
echo "# Generated at $(LC_ALL=C date)"
}
(cf_ips && echo "deny all; # deny all remaining ips") > /etc/nginx/allow-cloudflare-only.conf
# reload Nginx
# sudo systemctl reload nginx
Note that Cloudflare might update its IP addresses at some point. You’d better run this script regularly or use the cron job to update the IPs.
For example, put this script under the weekly cron job directory as /etc/cron.weekly/update-cfips
, give it execution permission with sudo chmod u+x /etc/cron.weekly/update-cfips
. And comment out the sudo systemctl reload nginx
line.
Then, you can include this configuration file in each server block or globally in Nginx.
For instance, in /etc/nginx/conf.d/example.com.conf
:
server {
listen 80;
listen [::]:80;
server_name example.com;
include /etc/nginx/allow-cloudflare-only.conf;
#...the rest of your configs here...
}
In this fashion, access the origin from IP addresses other than Cloudflare IPs will give a 403 Forbidden response.
More info on the ngx_http_access_module
which provides the allow/deny
to certain client addresses: http://nginx.org/en/docs/http/ngx_http_access_module.html.
By the way, if you want to log the IP address of the client, you cannot use the method stating in Reveal real IP for Nginx behind a reverse proxy by setting the set_real_ip_from
after setting the allow/deny
directives.
Instead, you can use map
directive to store the client’s IP into a variable (i.e. $real_client_ip
), and use that variable in the log:
# this goes before server section
map $http_x_forwarded_for $real_client_ip {
~^(\d+\.\d+\.\d+\.\d+) $1;
default $http_cf_connecting_ip;
}
# replace the default '$remote_addr' with the '$real_client_ip'
log_format custom_log_format '$real_client_ip - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_host" "$upstream_response_time"'
'"$http_referer" "$http_user_agent"';
server {
listen 80;
listen [::]:80;
server_name example.com;
include /etc/nginx/allow-cloudflare-only.conf;
access_log /var/log/nginx/access.log custom_log_format;
#...the rest of your configs here...
}