We’ve got customers who are using CloudFlare as a CDN Network and for Security in case their Word Press website gets a lot of traffic.  This gives them the ability to essentially cloak the really public IP address of their website and hide origin behind CloudFlare.

Drawback is you won’t get the public IP of the customer hitting the website, this can sometimes be a problem, like recently for a customer who uses Wordfence.  I had a double problem, we were running Varnish behind CloudFlare, we were getting some very good caching, but Wordfence was getting 127.0.0.1 because Varnish was the last IP it saw.  We needed to make some rules to allow things to pass through properly as well as provide the correct client’s public IP to WordPress and it’s plugins.

This is how it works CloudFlare add headers

X-Forwarded-For
CF-Connecting-IP

and our Varnish adds another header

X-Forwarded-By

Then the request goes to Apache, you pass through the CloudFlare mod_remoteip rules and the website gets the correct public IP.

What happens is RemoteIPInternalProxy is set to the Varnish IP which is 127.0.0.1, this is read from the header X-Forwarded-For, then it reads from X-Forwarded-By for  RemoteIPTrustedProxy which is CF, then if that passes it will assign the customers public IP to the Apache environment variable REMOTE_ADDR.

These are my configurations first for Varnish we add the following entry in the configuration

vi /etc/varnish/default.vcl

This is was added to vcl_recv section

 if (req.restarts == 0) {
   if (req.http.X-Forwarded-For) {
     set req.http.X-Forwarded-By = client.ip;
   }
 }

I next created an a configuration file for Apache

vi /etc/httpd/conf.d/cloudflare.conf

All files in the conf.d directory are automatically loaded at startup, and I wanted this config to be global for all websites on this server.  Our customer has multiple WordPress websites.  This file has the following contents, this contains all the CloudFlare proxy IPs along with our local Varnish server as a trusted internal proxy.

# CloudFlare IP Ranges. For definitive IP range lists see https://www.cloudflare.com/ips-v4 and https://www.cloudflare.com/ips-v6.

<IfModule !mod_remoteip.c>
LoadModule remoteip_module modules/mod_remoteip.so
</IfModule>

<IfModule mod_remoteip.c>
# CloudFlare Header
RemoteIPHeader X-Forwarded-For

# Trusted Proxy List
RemoteIPInternalProxy 127.0.0.1
RemoteIPTrustedProxy 103.21.244.0/22
RemoteIPTrustedProxy 103.22.200.0/22
RemoteIPTrustedProxy 103.31.4.0/22
RemoteIPTrustedProxy 104.16.0.0/12
RemoteIPTrustedProxy 108.162.192.0/18
RemoteIPTrustedProxy 141.101.64.0/18
RemoteIPTrustedProxy 162.158.0.0/15
RemoteIPTrustedProxy 172.64.0.0/13
RemoteIPTrustedProxy 173.245.48.0/20
RemoteIPTrustedProxy 188.114.96.0/20
RemoteIPTrustedProxy 190.93.240.0/20
RemoteIPTrustedProxy 197.234.240.0/22
RemoteIPTrustedProxy 198.41.128.0/17
RemoteIPTrustedProxy 199.27.128.0/21

# CloudFlare IPv6 Address Ranges
RemoteIPTrustedProxy 2400:cb00::/32
RemoteIPTrustedProxy 2405:8100::/32
RemoteIPTrustedProxy 2405:b500::/32
RemoteIPTrustedProxy 2606:4700::/32
RemoteIPTrustedProxy 2803:f800::/32
</IfModule>

After doing this restart both httpd and varnishd, then make a request.  I usually test with a php script that dumps out the server variables.

This customer was also behind a Pfsense server, however today we aren’t load balancing them, but this may end up being CF to Pfsense to Varnish to Apache, which will require some changes.  When that happens I’ll post a new configuration and what we did to keep the configuration passing the correct public IP through all caches.