Get Real Client IP from Cloudflare and pass to Apache or PHP using CF-Connecting-IP and Apache mod

How to Get the Real Client IP Address from Cloudflare in Apache or PHP

Last updated on | 14 replies

In this article we will learn how to get the real client IP from Cloudlfare (CF-Connecting-IP) and pass it on to PHP or Apache with mod_remoteip.

Introduction

Cloudflare is great as a quick-and-easy CDN and DDoS protection, but one downside is that the IP address seen by your web server will be that of the Clouflare proxy and not the actual client.

This can be a big security issue because your Apache access and error logs will only show the IP of the Cloudflare proxy and not the actual client. Also, if you have IP restrictions in place defined in .htaccess or a PHP script, these will not work.

Cloudflare sends the real client IP as CF-Connecting-IP in the HTTP header, and we can pass this on to PHP or Apache using mod_remoteip.

Note: Cloudflare’s own Apache mod mod_cloudflare is now redundant and discontinued as Apache’s own mod mod_remoteip performs the same function.

PHP Script

You can easily fetch the real client IP in PHP with no further configuration required.

echo $_SERVER["HTTP_CF_CONNECTING_IP"]; // real IP of visitor
echo $_SERVER["HTTP_CF_IPCOUNTRY"]; //country of visitor

If you do this, and the validity of the visiting IP address is important, you might need to verify that the $_SERVER["REMOTE_ADDR"] contains an actual Cloudflare proxy IP address, because anyone can fake the header if they are able to connect directly to the server. See “Trusted Proxies” down the page for more info.

Apache

This configuration was tested on Ubuntu 20.04 and 18.04, but the process should be similar for any Debian-based web servers.

This method also works for Virtual Hosts. If you have multiple sites hosted and some do not use Cloudflare, Apache will simply default back to the Remote Address (REMOTE_ADDR).

Enable remoteip mod

Firstly, enable the remoteip mod.

sudo a2enmod remoteip

Restart Apache:

sudo systemctl restart apache2

Edit Config and Define Trusted Proxies

In order to pass the real client IP address from Cloudflare to Apache, we need to define the RemoteIPHeader directive as CF-Connecting-IP in the remoteip configuration file /etc/apache2/conf-enabled/remoteip.conf.

Create the remoteip.conf configuration file:

sudo nano /etc/apache2/conf-enabled/remoteip.conf

Simply add RemoteIPHeader CF-Connecting-IP as the first line and then a list of trusted Cloudflare proxies below it (RemoteIPTrustedProxy). As it’s easy for hackers to spoof CF-Connecting-IP in the http header, we need to make sure that Apache knows which proxies to trust. Cloudflare keeps an updated list of these proxies here: Cloudflare IPv4 Proxies and Cloudflare IPv6 Proxies.

/etc/apache2/conf-enabled/remoteip.conf
RemoteIPHeader CF-Connecting-IP
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/12
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

Save and exit (press CTRL + X, press Y and then press ENTER)

Restart Apache:

sudo systemctl restart apache2

If Apache doesn’t detect CF-Connecting-IP in the HTTP header (e.g. if Cloudflare is turned off or not configured for a particular Virtual Host), it will fall back to the Remote Address (REMOTE_ADDR).

If Apache does detect CF-Connecting-IP but it is coming from an IP not defined in RemoteIPTrustedProxy (e.g. a hacker trying to spoof the headers), Apache will fall back to the Remote Address (REMOTE_ADDR).

Apache Access and Error Logs (optional)

You may not need to complete this step, but If you find that your access and error logs are showing the Cloudflare proxy IP instead of the remote user IP, you may need to do some further configuration.

Visit a page on your website in order to create an access log entry, then view the access log to see if it shows your IP or the Cloudfare proxy IP:

sudo tail /var/log/apache2/access.log -n 200

If it is not showing your IP, continue below.

In order for the real client IP (CF-Connecting-IP) to appear in access.log and error.log, we must make a small change to apache2.conf.

Edit apache2.conf:

sudo nano /etc/apache2/apache2.conf

Press CTRL + W and search for LogFormat.

The default log format should look something similar to below:

/etc/apache2/apache2.conf
LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent

The %h variable in red here is the Remote IP. We just need to change this to %a, which is the Client IP as defined by the mod_remoteip module.

/etc/apache2/apache2.conf
LogFormat "%v:%p %a %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
LogFormat "%a %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%a %l %u %t \"%r\" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent

Save and exit (press CTRL + X, press Y and then press ENTER)

Restart Apache:

sudo systemctl restart apache2

To view the access log to see if it is now reporting CF-Connecting-IP:

sudo tail /var/log/apache2/access.log -n 200

Make sure that the Cloudlfare Proxy IP does not appear in the access log, but instead your own client IP.

If Apache doesn’t detect CF-Connecting-IP in the HTTP header (e.g. if Cloudflare is turned off or not configured for a particular Virtual Host), the log will fall back to the Remote Address (REMOTE_ADDR).

If Apache does detect CF-Connecting-IP but it is coming from an IP not defined in RemoteIPTrustedProxy (e.g. a hacker trying to spoof the headers), the log will fall back to the Remote Address (REMOTE_ADDR).

Let me know if this helped. Follow me on Twitter, Facebook and YouTube, or 🍊 buy me a smoothie.

14 replies

Leave a reply

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

  1. Mine seems to work fine with the default %h in the LogFormat line so I guess I’ll just leave it as is. 😅

    1. Yes, something has changed since I wrote this article. It seems you no longer need to complete this step. Article updated.

  2. Don’t forget to add your own IPv4 network (and IPv6 too, if you have it), otherwise you’ll just see your router’s LAN address appearing in the Apache logs.

    If you use any of the following RFC1918 addresses for your LAN IP addressing, just add the whole block to exclude it, viz:

    10.0.0.0/8
    173.172.16.0.0/12
    192.168.0.0/16

    If you use IPv6, just add the /48 prefix that your ISP assigns to you, and that will also cover any subnets you carved out of a /64 as well (if you were that adventurous to begin with).

  3. Awesome life-saver of an article — we suddenly had no way to identify bad actors on any of our websites – which kind of defeats the purpose of Cloudflare as far as a WAF goes… mod_remoteip replacing mod_cloudflare did the trick! In a sea of useless repeated affiliate nonsense – this post was an oasis – the clouds parted, the sun shone and all was well…

  4. I could not this working with the Cloudflare guide, it said add this /etc/apache2/conf-available/remoteip.conf
    you said /etc/apache2/conf-enabled/remoteip.conf and it works 🙂
    Thank you sooooo much 🙂

  5. Hi,
    how is it possible to get the cd country code into the response header. I tryed a lot but didnt find a working solution.

  6. Clear and simple instructions/explanations, like we love (at least I love it !).
    It indeed was very helpful. Thanks.

  7. Hi there,

    I want to ask, if we use this mod today, can past log (yesterday log) change to show the origin IP Address?

    Thank you. 🙂