Passing original visitor IP to server when using Cloudflare CDN with cPanel
When your website runs on Cloudflare CDN, then all the IP addresses visible to any server software are actually Cloudfare IP addresses, not actual visitor's IP addresses.
This can be very confusing when some modules make use of visitor IP, like access logs or some other feature that is customized based on the visitor's IP address.
So, we were actually using Maxmind's GeoIP database to capture visitor's IP address and then based on that, auto-populating the address fields while doing the checkout. This considerably reduces the checkout time for the customers and helps in better user experience.
The issue was that our website was making use of Cloudflare CDN, so the visitor's real IP was never visible to any server module.
Our server uses cPanel, and there was no readymade solution to solve this challenge.
Further analysis revealed that Apache provides a module called remoteIP, which can be used to alter the way Apache captures the user's real IP.
Cloudflare provides a guide to handle this situation for Apache, but unfortunately, those instructions do not work seamlessly in the case of cPanel.
So we had to dig deeper.
The following were the steps which solved it for us:
1. install apache remoteIP module using easyapache 4 on WHM.
2. Apache on WHM is configured to have its own config file for every module. Actually, when apache runs, its main config file just includes all other modules' config files as well. So we can place any apache config directives in any of the modules' config files.
3. Here we open and modify Modify Apache's config file for remote IP.
4. Edit the file /etc/apache2/conf.modules.d/NNN_mod_remoteip.conf and add following content at the bottom. CF-Connecting-IP is the http/http2 header entry in which Cloudflare stores visitor's real IP. We are now trying to make it available to Apache. Do not worry about the cryptic looking IP address entries, just copy and paste all of them. These IP addresses are actually Cloudflare IP addresses, and we are instructing Apache to bypass those IPs as trusted proxies.
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
5. Last step: Change the variable in apache's main config file (/etc/apache2/conf/httpd.conf) so that it does not use the old IP and uses the IP returned by remoteIP module, for all logging modules.
You will see something like this:
<IfModule mod_log_config.c>
LogFormat "%v:%p %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combinedvhost
<IfModule logio_module>
LogFormat "%v %{%s}t %I .\n%v %{%s}t %O ." bytesvhost
</IfModule>
LogFormat "%v:%p %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%v:%p %h %l %u %t \"%r\" %>s %b" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent
<IfModule logio_module>
CustomLog "|/usr/local/cpanel/bin/splitlogs --dir=/etc/apache2/logs/domlogs --main=purple1.purpletreecloud.net --suffix=-bytes_log" bytesvhost env=!isproxyrequest
</IfModule>
CustomLog "|/usr/local/cpanel/bin/splitlogs --dir=/etc/apache2/logs/domlogs --main=purple1.purpletreecloud.net --mainout=/etc/apache2/logs/access_log" combinedvhost env=!isproxyrequest
</IfModule>
Change this to
<IfModule mod_log_config.c>
LogFormat "%v:%p %a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combinedvhost
<IfModule logio_module>
LogFormat "%v %{%s}t %I .\n%v %{%s}t %O ." bytesvhost
</IfModule>
LogFormat "%v:%p %a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%v:%p %a %l %u %t \"%r\" %>s %b" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent
<IfModule logio_module>
CustomLog "|/usr/local/cpanel/bin/splitlogs --dir=/etc/apache2/logs/domlogs --main=purple1.purpletreecloud.net --suffix=-bytes_log" bytesvhost env=!isproxyrequest
</IfModule>
CustomLog "|/usr/local/cpanel/bin/splitlogs --dir=/etc/apache2/logs/domlogs --main=purple1.purpletreecloud.net --mainout=/etc/apache2/logs/access_log" combinedvhost env=!isproxyrequest
</IfModule>
This means you have to replace all instances of %h with %a in "mod_log_config.c" section.
6. That's it. Now all software and modules on your server are able to see real IP addresses of visitors.