When Cloudflare's proxy sits in front of your site, every request to your server comes from a Cloudflare IP, not the visitor's real one. Your logs, your WordPress plugins, and any IP-based logic in your code will all see Cloudflare's address unless something restores the original. The fix is almost always to read the CF-Connecting-IP header, which Cloudflare adds to every proxied request and which contains the visitor's real IP.
This only matters if proxy mode is turned on for the domain. You manage that from Goodies > Cloudflare CDN in the portal, not a cloudflare.com dashboard, since we don't expose one to customers. If proxy is off for a domain, traffic goes straight to your server and this isn't an issue.
Why the IP changes at all
A reverse proxy terminates the visitor's connection and opens its own connection to your server. From your server's point of view, every visitor is Cloudflare. That's normal for any CDN or proxy setup, not a bug. Cloudflare compensates by adding two headers to the request it forwards to your server:
CF-Connecting-IP: the visitor's real IP address. This is the one you want.X-Forwarded-For: a comma-separated chain that can include the visitor IP plus any proxies in between. UseCF-Connecting-IPinstead when it's available; it's simpler and specific to Cloudflare.
Both headers are added automatically for proxied domains. There's no setting to turn this on or off, and no per-record control since the proxy toggle lives at the domain level, not on individual DNS records.
Fixing WordPress IP detection
WordPress core and most plugins call $_SERVER['REMOTE_ADDR'] when they need a visitor's IP, for things like comment logging, login attempt limiting, or basic analytics. On a proxied domain, that variable holds Cloudflare's IP, not the visitor's. The standard fix is a small snippet that overwrites REMOTE_ADDR with the value from CF-Connecting-IP early in the request:
if (!empty($_SERVER['HTTP_CF_CONNECTING_IP'])) {
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP'];
}
Note the header name gets normalized to HTTP_CF_CONNECTING_IP in PHP's $_SERVER array; dashes become underscores and it's prefixed with HTTP_. Add this near the top of your theme's functions.php, or better, in a small standalone plugin so it survives theme switches. Many security and firewall plugins (anything that blocks IPs or rate-limits logins) ship a "trust Cloudflare" or "behind a proxy" setting that does exactly this under the hood; check the plugin's settings before writing your own snippet, since duplicating the fix can occasionally cause conflicts.
If you manage the site through cPanel and want to confirm what your server is actually receiving, Metrics > Errors in cPanel shows the raw error log, and a quick error_log($_SERVER['HTTP_CF_CONNECTING_IP']) call in a test script will confirm the header is present and correctly populated before you build logic around it.
Fixing server logs and custom code
Apache and most application logs record whatever IP made the TCP connection, which on a proxied domain is Cloudflare's. If you're writing custom PHP, Node, or another backend and need the real visitor IP for rate limiting, geolocation, or audit logs, read the header directly instead of relying on the connection's source IP:
$visitor_ip = $_SERVER['HTTP_CF_CONNECTING_IP'] ?? $_SERVER['REMOTE_ADDR'];
The fallback to REMOTE_ADDR matters: if the domain's proxy is ever switched off, or if the request somehow reaches your server directly, the header won't be present and you still want a usable value.
One thing to watch for: never trust an X-Forwarded-For or CF-Connecting-IP-style header on a server that isn't actually behind Cloudflare, since anyone can forge those headers directly. On a domain proxied through our Cloudflare CDN page, the header is trustworthy because Cloudflare's edge sets it and strips any client-supplied version of the same header before forwarding.
Checking whether a request actually came through Cloudflare
If logs still look wrong after adding the fix, confirm proxy mode is actually on for that domain. Go to Cloudflare CDN under Goodies in the portal, find the domain, and check the proxy toggle. It's on by default for hosted domains, but if it's off, requests bypass Cloudflare entirely and there's no CF-Connecting-IP header to read, meaning REMOTE_ADDR was correct all along and the snippet above is a harmless no-op.
It's also worth checking whether caching is masking the issue rather than the IP logic itself. A cached page won't re-run PHP for each visitor, so any IP-dependent logic embedded in cached output can look stale. Purge cache from the same Cloudflare CDN page while testing, and see the full settings reference in every Cloudflare setting explained for how caching and proxying interact.
When to open a ticket
If you've confirmed proxy is on, added the header fix, and a specific plugin or script still logs the wrong IP, open a ticket from Support in the portal. Include the plugin name and a sample log line; a real person can check server-side request headers directly to confirm what's arriving versus what's being recorded.