I’m currently using LogDNA for gathering Nginx logs. However, I can only see IPs from Cloudflare by default in the logs as my server was proxied by Cloudflare. Let’s see how to reveal the real IP address of the client in the logs behind such reverse proxy server by using ngx_http_realip_module
.
Check ngx_http_realip_module
First, let’s see whether the ngx_http_realip_module
was installed.
nginx -V 2>&1 | egrep --color -o 'http_realip_module'
nginx -V 2>&1 | egrep --color -o 'realip_module'
If your Nginx was installed by apt
, this module should be compiled by default.
Edit your Nginx configuration file such as nginx.conf
or any virtual domain configuration. For example:
sudo nano /etc/nginx/nginx.conf
Set the following directive in the http
, server
, or location
context as follows:
real_ip_header X-Forwarded-For;
If you’re using Cloudflare, try the following:
real_ip_header CF-Connecting-IP;
Some cloud reverse proxy passes on header named X-Real-IP
, so try the following:
real_ip_header X-Real-IP;
Get real IPs from reverse proxy
We need to define the trusted IPs that are known to send correct replacement addresses. Typically we need to add upstream server IPs using following syntax:
set_real_ip_from ipv4_address;
set_real_ip_from ipv6_address;
set_real_ip_from sub/net;
set_real_ip_from CIDR;
For example:
set_real_ip_from 123.123.123.123;
set_real_ip_from 123.123.123.0/24;
For Cloudflare’s IPs, you can use the script from https://github.com/ergin/nginx-cloudflare-real-ip to automatically config/update the IPs.
Restart Nginx server
After setting your configuration files, don’t forget to restart/reload Nginx. It’s better to check the configs before reload Nginx:
sudo nginx -t
sudo systemctl reload nginx
A helper script for Cloudflare
Here’s a shell script I used to configure my Nginx server that get user’s real IPs from Cloudflare proxies:
#!/bin/bash
set -e
cf_ips() {
echo "# https://www.cloudflare.com/ips"
for type in v4 v6; do
echo "# IP$type"
curl -s "https://www.cloudflare.com/ips-$type/" | sed "s|^|set_real_ip_from |g" | sed "s|\$|;|g"
echo
done
echo "real_ip_header CF-Connecting-IP;"
echo "# Generated at $(LC_ALL=C date)"
}
cf_ips > /etc/nginx/cloudflare-ips.conf
sudo systemctl reload nginx
Sample output like:
# https://www.cloudflare.com/ips
# IPv4
set_real_ip_from 173.245.48.0/20;
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 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/12;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 131.0.72.0/22;
# IPv6
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 2a06:98c0::/29;
set_real_ip_from 2c0f:f248::/32;
real_ip_header CF-Connecting-IP;
# Generated at Sat Dec 26 17:03:29 CST 2020
Then, you can include the generated /etc/nginx/cloudflare-ips.conf
file in your virtual domain configuration file where required:
include /etc/nginx/cloudflare-ips.conf;
After that, the $remote_addr
variable should hold the correct IP address from the client.
Also, it’s better to set a cron job to run the above script to update the Cloudflare IPs regularly.
A little more
However, I found that set_real_ip_from
cannot work together with allow/deny
configuration.
Since my Nginx server is just one level behind Cloudflare, I’m more intended to just allow requests from Cloudflare IPs, and get the real client’s IP from the $http_x_forwarded_for
. For system logs, we can adapt the Nginx [log_format](https://docs.nginx.com/nginx/admin-guide/monitoring/logging/)
to log forwarded IPs using the $http_x_forwarded_for
instead of the $remote_addr
. See this post to find out how that works.