Get real visitor IP from CloudFlare to Nginx and Apache

This document will show you how to get real visitor ip to your main server logs, server statistics, etc, when your site is behind CloudFlare. This will explain 2 different situations:

CloudFlare > your main (source) server
When you have your main site/server and in front of it you have CloudFlare, most common setup.

CloudFlare > your cache server (e.g Nginx) > your main (source server)
When you have your main server (e.g Apache), another Nginx cache server (reverse proxy server) that loads data from main server, and CloudFlare getting data from your Nginx "cache" server.

For those unfamiliar with, or confused by the terms:

Apache is many times referred to http or httpd

Nginx web server is sometimes also referred to cache server or reverse proxy server, because it can cache data between user and main server that hosts the data, but not always, Nginx can also be a standalone server that fully replaces Apache.

Upstream server (apache in these examples) is the "source" server, the main server from which the data originates, but there can be multiple "upstream" servers on top of each other so upstrream server is technically more correct than source server.

Check Apache or Nginx logs

vi /var/log/httpd/access_log
vi /var/log/nginx/access.log

If they seem to contain same visitor IP addresses for all requests, you can do a whois on these IPs, if they all show CloudFlare then you need to change below configs to get real ip

For CloudFlare > main server (apache)

If you have your main server running Apache and you configure CloudFlare for it, without any cache server in between them (most common setup), then ...

1) Go to Apache config folder: cd /etc/httpd/conf.d

2) Create a new config file: vi cloudflare_real_ip.conf

3) Paste below code in it:

# detect real remote ip based on what the trusted proxy specifies by headers
# when hit comes here directly from cloudflare, get real ip from CF-Connecting-IP header
# and specify the trusted servers to provide this real ip
# when hit comes here from another of our nginx proxies, we have a different file

RemoteIPHeader CF-Connecting-IP

# list of cloudlfare IPs taken from: 
# https://www.cloudflare.com/ips-v4
# https://www.cloudflare.com/ips-v6

RemoteIPTrustedProxy 173.245.48.0/20
RemoteIPTrustedProxy 103.21.244.0/22
RemoteIPTrustedProxy 103.22.200.0/22
RemoteIPTrustedProxy 103.31.4.0/22
RemoteIPTrustedProxy 141.101.64.0/18
RemoteIPTrustedProxy 108.162.192.0/18
RemoteIPTrustedProxy 190.93.240.0/20
RemoteIPTrustedProxy 188.114.96.0/20
RemoteIPTrustedProxy 197.234.240.0/22
RemoteIPTrustedProxy 198.41.128.0/17
RemoteIPTrustedProxy 162.158.0.0/15
RemoteIPTrustedProxy 104.16.0.0/13
RemoteIPTrustedProxy 104.24.0.0/14
RemoteIPTrustedProxy 172.64.0.0/13
RemoteIPTrustedProxy 131.0.72.0/22

RemoteIPTrustedProxy 2400:cb00::/32
RemoteIPTrustedProxy 2606:4700::/32
RemoteIPTrustedProxy 2803:f800::/32
RemoteIPTrustedProxy 2405:b500::/32
RemoteIPTrustedProxy 2405:8100::/32
RemoteIPTrustedProxy 2a06:98c0::/29
RemoteIPTrustedProxy 2c0f:f248::/32

That code is already commented, you tell Apache 2 things: from what http header tag to take the real visitor IP (CF-Connecting-IP in this case), and what are the CloudFlare IPs that can pass this header tag to your main server (RemoteIPTrustedProxy values). Without this, any normal visitor to your server can specify a fake CF-Connecting-IP value and your server would use that.

4) after you added the above code inside cloudflare_real_ip.conf, restart apache: service httpd restart

Note 1

The above directives uses apache module called remoteip_module and should be enabled on most servers, you can test with command: apachectl -M | grep remoteip_module

It should return: remoteip_module (shared)

Note 2

The above example created a separate .conf file in the main apache config directory, that means these directives will be applied to all your sites ! But if you have multiple sites there and not all of them are using CloudFlare, you can rename that config to "include_cloudflare_real_ip" (without .conf at the end !) and include it inside virtual host config of each site that is behind CloudFlare:

<VirtualHost *:80>
	ServerName mysite.com
	DocumentRoot /var/www/html/mysite.com
	Include include_cloudflare_real_ip
</VirtualHost>

For CloudFlare > Nginx cache server > Main server

This is only needed when you have a cache server between your main server and cloudflare.

Note that some setups like Plesk Panel do this by default, places an Nginx cache server in front of your main Apache web server, these can run on same machine / computer, and Nginx will use port 80/443 and Apache an alternative custom port.This is done to take load off from Apache by caching requests/files in Nginx

Regardless if Nginx cache server and Apache main server are on same machine or on 2 different ones ...

First make Nginx take real IP from CloudFlare

1) go to Nginx config folder: cd /etc/nginx/conf.d

2) create a new config file: vi include_real_ip_from_cloudflare.conf

3) paste this config:

# nginx config to get real ip privided by cloudflare
# get real ip from this header tag name:

real_ip_header CF-Connecting-IP;

# allow these below IPs to tell us the real IP
# live list of ips was taken from: 
# https://www.cloudflare.com/ips-v4
# https://www.cloudflare.com/ips-v6

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/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 131.0.72.0/22;

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;

Just like with previous Apache setup, real_ip_header tells Nginx from what header tag name to take the real IP (tag name CF-Connecting-IP in this case), and set_real_ip_from tells Nginx what visitor IPs can specify this header tag, so that another malicious visitor cannot pass a fake "real ip" in this header tag.

4) inside the same above nginx conf file, or inside server { ... }, or inside location / { ... } you need to also tell Nginx to pass the real IP forward / upper to the main Apache server, by this config line:

proxy_set_header X-Real-IP $remote_addr;

Our main apache server will then take real visitor IP from this X-Real-IP tag specified by Nginx cache server, and the $remote_addr should contain real visitor ip only after the above cloudflare code, otherwise it will pass a CloudFlare IP forward to main Apache server.

5) on your apache server, go to apache config folder: cd /etc/httpd/conf.d

6) create a new config file: vi real_ip_from_own_cache_servers.conf

7) paste this config:

# detect real remote ip based on what the trusted proxy/cache server specifies by headers
# when hit comes from our own nginx cache servers, can get real ip from X-Real-IP
# and specify the trusted servers to provide this real ip
# when hit comes here directly from CoudFlare, we have a different file

RemoteIPHeader X-Real-IP

RemoteIPTrustedProxy my-cache-server.mysite.com
RemoteIPTrustedProxy my-cache-server-2.mysite.com

RemoteIPTrustedProxy can be the host name(s) or ip(s) of your server running Nginx, where requests come from to the main apache server. Again, this is to know who can specify a certain "real visitor ip" and what tag name contains this value. Usually you only have one RemoteIPTrustedProxy

8) restart Apache: service httpd restart

You should now start seeing real visitor IPs in your main server logs:

vi /var/log/httpd/access_log
# or
vi /var/log/nginx/access.log

All the above should already return correct visitor ip even in other enviroments like php variables: $_SERVER['REMOTE_ADDR']