Wrong Visitor IP in cPanel Logs? Fix It Behind Cloudflare
Turn on Cloudflare's orange cloud and suddenly every visitor on your server looks like the same handful of IP addresses. Your AWStats reports show one "user" generating half your traffic. Fail2Ban bans a Cloudflare data-center IP and locks out your entire site. A WordPress commenter's IP shows up as 104.x.x.x instead of their real address. None of this is a bug in your hosting - it's what happens when a reverse proxy sits in front of your server and nothing on the origin side has been told to look past it.
Symptom: every request looks like it's coming from Cloudflare
Once a domain is proxied through Cloudflare (or any CDN/reverse proxy), the TCP connection to your VPS or cPanel server originates from Cloudflare's edge network, not from the visitor's browser. That means:
$_SERVER['REMOTE_ADDR']in PHP returns a Cloudflare IP, not the visitor's IP.- Apache/Nginx access logs and AWStats/Webalizer in cPanel show a small pool of repeating Cloudflare IPs instead of real visitors.
- WordPress plugins that log IP (comment forms, contact forms, login logs, security plugins) record the wrong address.
- cPanel's IP Blocker, cPHulk, or a WHM firewall rule aimed at a specific abusive visitor does nothing, because that visitor's real IP never touches your server.
- Worst case: Fail2Ban or CSF sees a spike of "bad" requests from a Cloudflare IP and bans it - taking down access for every visitor behind that edge node, including legitimate traffic.
Cause: your origin server never unwraps the real client IP
Cloudflare (and most CDNs) forward the visitor's real IP in an HTTP header - usually CF-Connecting-IP, alongside the more general X-Forwarded-For. But Apache, Nginx, PHP, and every tool built on top of them (AWStats, WordPress, Fail2Ban) only trust REMOTE_ADDR, the actual TCP peer address, unless you explicitly configure them to read that header instead - and only when the connection genuinely comes from Cloudflare's published IP ranges.
Skipping that "only trust it from Cloudflare's ranges" part is the part people get wrong when they try to fix this with a plugin alone: trusting X-Forwarded-For blindly lets anyone spoof their IP by sending a fake header directly to your server's IP, bypassing Cloudflare entirely.
Fix 1: restore the real IP at the web server level (cPanel/WHM, Apache)
This is the fix that makes AWStats, Webalizer, cPanel's raw access logs, and cPHulk all see the correct IP at once, because it rewrites REMOTE_ADDR before anything else touches the request.
- In WHM, go to Software > EasyApache 4 and confirm
mod_remoteipis installed (on CloudLinux servers it's usually already available asmod_cloudflareor bundled with CageFS-aware Apache builds). - Configure it with Cloudflare's current IP ranges, pulled fresh from
https://www.cloudflare.com/ips-v4andhttps://www.cloudflare.com/ips-v6- don't hardcode an old list, Cloudflare adds ranges occasionally. - Set the trusted header to
CF-Connecting-IP(more reliable than the genericX-Forwarded-For, which can contain a chain of proxies). - Restart Apache:
whmapi1 restartservice httpdor via WHM > Restart Services.
If you're on Nginx (a reverse proxy in front of Apache, or a standalone LEMP VPS), the equivalent is the ngx_http_realip_module:
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
# ... one line per Cloudflare range ...
real_ip_header CF-Connecting-IP;
real_ip_recursive on;
Put this in http {} or a dedicated conf.d/cloudflare.conf file, then nginx -t && systemctl reload nginx.
Fix 2: make WordPress see the real IP too
Fixing Apache/Nginx fixes REMOTE_ADDR at the OS level, which covers most cases. But some WordPress plugins read $_SERVER['HTTP_CF_CONNECTING_IP'] or HTTP_X_FORWARDED_FOR directly instead of relying on REMOTE_ADDR. For those, a small must-use plugin is the cleanest fix - it runs before every other plugin and keeps the logic in one place instead of scattered across theme functions:
<?php
// wp-content/mu-plugins/restore-real-ip.php
if (!empty($_SERVER['HTTP_CF_CONNECTING_IP'])) {
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP'];
}
Only add this if you've already restricted access at the web-server level to Cloudflare's ranges (Fix 1) - otherwise a visitor who bypasses Cloudflare and hits your origin IP directly could set that header themselves and spoof any address they like.
Fix 3: stop Fail2Ban and CSF from banning Cloudflare instead of the attacker
If Fail2Ban or CSF/LFD is watching Apache's access log and that log still shows Cloudflare IPs (because Fix 1 hasn't been applied yet, or a jail reads raw connection data), it will eventually ban a Cloudflare edge node - and that ban blocks every visitor routed through it, not just the attacker.
| Symptom | Likely cause | Fix |
|---|---|---|
| Whole site suddenly unreachable for many visitors at once | CSF/Fail2Ban banned a Cloudflare IP range | Whitelist Cloudflare's ranges in CSF's csf.pignore/csf.allow, or exclude them in the Fail2Ban jail |
| Bans "work" but the attacker keeps getting through | Fail2Ban is reading REMOTE_ADDR before Fix 1 is applied | Apply Fix 1 first, then bans will target the real attacker IP |
| cPHulk locks out a shared office/mobile IP repeatedly | Multiple attackers or scanners share one Cloudflare-facing IP pool upstream (e.g. carrier-grade NAT), unrelated to Cloudflare | Check WHM > cPHulk Brute Force Protection logs for the real originating IP once headers are trusted |
In WHM, add Cloudflare's published ranges to CSF's ignore list under ConfigServer Security & Firewall > csf.pignore so LFD never auto-bans them, while still letting you manually block a genuinely abusive real IP once you can see it.
Prevention
- Re-pull Cloudflare's IP range list every few months -
curl https://www.cloudflare.com/ips-v4- and update your Apache/Nginx trusted-proxy config; ranges do change. - Never trust
X-Forwarded-FororCF-Connecting-IPfrom a server that also accepts direct traffic on its origin IP. Lock the firewall (CSF/UFW) so port 80/443 only accepts connections from Cloudflare's ranges plus your own known IPs. - After any EasyApache 4 rebuild, confirm
mod_remoteipis still enabled - profile rebuilds can silently drop modules that weren't in the saved profile. - Test with a quick check: visit your site through Cloudflare from your phone (mobile data, not home Wi-Fi) and confirm the IP in your latest access log entry matches your phone's actual public IP, not a 104.x/172.x Cloudflare address.
Frequently asked questions
How do I know if this is actually my problem?
Check your latest Apache or Nginx access log entry after visiting your own site: run tail -20 /usr/local/apache/domlogs/yourdomain.com or the cPanel equivalent, then compare the IP shown to your real public IP (search "what is my ip"). If they don't match and your site is proxied through Cloudflare, this is exactly the issue.
Is X-Forwarded-For or CF-Connecting-IP more reliable?
Use CF-Connecting-IP when you're specifically behind Cloudflare. X-Forwarded-For can contain a comma-separated chain of proxy IPs if multiple proxies are involved, and picking the wrong position in that chain is a common source of bugs. CF-Connecting-IP is always just the one real visitor IP.
Will this break anything if I don't have Cloudflare in front of my site?
Don't apply mod_remoteip, real_ip_module, or the must-use plugin unless you're actually proxying through Cloudflare (or another CDN). Without a proxy in front, REMOTE_ADDR is already correct, and trusting a spoofable header from direct visitors would let anyone fake their IP.
Can I just disable Cloudflare's proxy (grey cloud) instead of fixing this?
You can, but you'll lose Cloudflare's DDoS protection, caching, and CDN benefits for that record. For most sites it's worth spending the 15 minutes to configure real-IP restoration properly rather than turning off the proxy.
Does this affect email (MX) records too?
No - you should never proxy an MX record through Cloudflare's orange cloud in the first place; mail servers need to connect directly to your server's real IP. This fix is specifically for HTTP/HTTPS traffic (the A/CNAME record for your website).