Custom graphic showing the Cloudflare logo on an orange background with the text “Cloudlfare Real Client IP.”

Retrieving the Real Client IP from Cloudflare in Apache, Nginx and PHP

Last updated on | 14 replies

Introduction

When using Cloudflare for CDN and DDoS protection, your web server may see the IP address of a Cloudflare proxy instead of the actual visitor’s IP. This can interfere with logging, security measures, and analytics. In this updated guide, we show you how to retrieve the real client IP using Cloudflare’s CF-Connecting-IP header. The guide covers configurations for PHP, Apache (with mod_remoteip), and Nginx (using the real IP module), ensuring you always have an accurate view of your visitors.

PHP

To access the real client IP in PHP, Cloudflare includes the visitor’s IP address in the CF-Connecting-IP header. You can access this header directly in your PHP code. For example:

example.php
<?php
// Retrieve the real client IP
if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
    $clientIp = $_SERVER['HTTP_CF_CONNECTING_IP'];
    echo 'Client IP: ' . htmlspecialchars($clientIp);
} else {
    echo 'Client IP: ' . htmlspecialchars($_SERVER['REMOTE_ADDR']);
}

// Optionally, display the visitor's country code if provided
if (isset($_SERVER['HTTP_CF_IPCOUNTRY'])) {
    echo ' (Country: ' . htmlspecialchars($_SERVER['HTTP_CF_IPCOUNTRY']) . ')';
}
?>

Security Note: Before trusting the CF-Connecting-IP header, verify that REMOTE_ADDR matches a genuine Cloudflare proxy IP. This helps prevent header spoofing. You can find the latest Cloudflare proxy ranges at Cloudflare IPv4 Proxies and Cloudflare IPv6 Proxies.

Apache

If you already have a working Apache webserver, you only need to enable and configure the mod_remoteip module to use Cloudflare’s CF-Connecting-IP header. This ensures your logs and IP-based rules accurately reflect the visitor’s real IP.

Step 1: Enable mod_remoteip

Depending on your Linux distribution, follow the appropriate instructions below:

Debian/Ubuntu

Enable the module:

sudo a2enmod remoteip
sudo systemctl restart apache2

RHEL/CentOS/Fedora/AlmaLinux/Rocky Linux

If needed, ensure the module is loaded by adding the following to your Apache configuration (e.g., in /etc/httpd/conf.modules.d/):

LoadModule remoteip_module modules/mod_remoteip.so

Then restart Apache (often called httpd):

sudo systemctl restart httpd

openSUSE

If mod_remoteip is available, enable it by ensuring the following line is present in your configuration (e.g., in /etc/sysconfig/apache2 or a dedicated config file):

LoadModule remoteip_module /usr/lib64/apache2/mod_remoteip.so

Then restart Apache (apache2):

sudo systemctl restart apache2

Arch Linux

Edit /etc/httpd/conf/httpd.conf or a file under conf.modules.d/ to ensure mod_remoteip is loaded:

LoadModule remoteip_module modules/mod_remoteip.so

Then restart Apache:

sudo systemctl restart httpd

Step 2: Configure Trusted Proxies

Edit or create the configuration file (/etc/apache2/conf-enabled/remoteip.conf on Debian/Ubuntu, /etc/httpd/conf.d/remoteip.conf on RHEL-based systems, etc.) to instruct Apache to use the CF-Connecting-IP header. Include Cloudflare’s IP ranges and any private network ranges you wish to exclude from your logs.

You can find the latest Cloudflare proxy ranges at Cloudflare IPv4 Proxies and Cloudflare IPv6 Proxies.

Example configuration:

/etc/apache2/conf-enabled/remoteip.conf
RemoteIPHeader CF-Connecting-IP

# Cloudflare IPv4 addresses
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

# Cloudflare IPv6 addresses
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

# Exclude common private network ranges (optional)
RemoteIPTrustedProxy 10.0.0.0/8
RemoteIPTrustedProxy 172.16.0.0/12
RemoteIPTrustedProxy 192.168.0.0/16

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

Restart Apache to apply your changes:

sudo systemctl restart apache2

Note: On some distributions (like CentOS/RHEL), the service is named httpd. Older Apache setups may also require modifying the log format (e.g., replacing %h with %a).

Nginx

If you already have a working Nginx webserver, you simply need to configure the built-in ngx_http_realip_module to use Cloudflare’s CF-Connecting-IP header so your logs reflect the real client IP.

Step 1: Verify the Real IP Module

The real IP module is included by default in most Nginx installations. You can verify by checking nginx -V or reviewing your configuration files.

Step 2: Configure Trusted Proxies

Add the following directives to your Nginx configuration (inside the http block or in a server block file located in /etc/nginx/nginx.conf, /etc/nginx/conf.d/, etc.). These lines instruct Nginx to trust Cloudflare’s IP ranges.

The extra lines to exclude common private network ranges are optional—include them if you wish to ignore local LAN traffic.

You can find the latest Cloudflare proxy ranges at Cloudflare IPv4 Proxies and Cloudflare IPv6 Proxies.

/etc/nginx/nginx.conf
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;

# Cloudflare IPv6 ranges
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;

# Optionally exclude common private network ranges
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.16.0.0/12;
set_real_ip_from 192.168.0.0/16;

real_ip_header CF-Connecting-IP;

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

This setup tells Nginx to trust requests coming from Cloudflare’s IP ranges and to use the CF-Connecting-IP header for determining the real client IP.

Step 3: Reload Nginx

Reload Nginx to apply your changes:

sudo systemctl reload nginx

Conclusion

By configuring PHP, Apache, and Nginx to recognize Cloudflare’s CF-Connecting-IP header, you ensure that your server logs and security measures capture the real client IP. This leads to more accurate analytics, improved security, and easier troubleshooting.

Additional Tips:

  • Always verify that REMOTE_ADDR is within Cloudflare’s ranges before trusting the header value.
  • If you use private networks (e.g., 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), add these ranges to avoid logging your internal IP. This is optional and only needed if your server receives non-Cloudflare traffic.
  • Changes to these configurations are not retroactive; past logs will not be updated.
  • If you’re using Cloudflare tunnels or additional proxies, adjust the configurations accordingly.

This guide has helped many users identify malicious traffic and secure their logs. If you encounter issues, double-check your configuration and refer to Cloudflare’s official documentation for the most up-to-date proxy ranges.

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. 🙂