If a WordPress security plugin warns you about "IP detection settings," or your PHP app suddenly logs the same handful of IPs for every visitor, the cause is almost always the same: a reverse proxy like Cloudflare sits between your visitors and your server. The short version — keep REMOTE_ADDR as the default, never trust HTTP_CLIENT_IP blindly, and read CF-Connecting-IP only when you have confirmed the request really came through Cloudflare. Here is the why behind each of those.
What each value actually means
In PHP these arrive as $_SERVER entries. They are not interchangeable — they come from completely different places, and that difference is the whole story:
REMOTE_ADDR— the IP address of whoever opened the TCP connection to your server. The web server fills this in from the socket itself, so a client cannot forge it. This is the one trustworthy value.HTTP_CLIENT_IPandHTTP_X_FORWARDED_FOR— ordinary HTTP request headers. AnyHTTP_*entry in$_SERVERis just a header the client sent, which means anyone can set it to anything with a single line ofcurl.HTTP_CF_CONNECTING_IP— theCF-Connecting-IPheader that Cloudflare adds, containing the real visitor IP. Trustworthy only when you can guarantee the request passed through Cloudflare.
Why behind Cloudflare REMOTE_ADDR is "wrong"
When Cloudflare proxies your domain, your visitors connect to Cloudflare's edge, and Cloudflare opens a fresh connection to your origin server. From your server's point of view, the visitor is Cloudflare — so REMOTE_ADDR is a Cloudflare edge IP, the same for thousands of different people. Nothing is broken; the proxy is simply doing its job.
To hand you the original visitor, Cloudflare attaches the CF-Connecting-IP header on every request. So behind Cloudflare the correct source is $_SERVER['HTTP_CF_CONNECTING_IP'], falling back to REMOTE_ADDR when that header is absent.
The security trap: why you can't just trust the header
It is tempting to write "read HTTP_CLIENT_IP or X-Forwarded-For, otherwise REMOTE_ADDR" and move on. That is the exact pattern that gets sites compromised. Because those are client-supplied headers, an attacker can send:
curl https://your-site.example/wp-login.php \
-H "X-Forwarded-For: 8.8.8.8" \
-H "Client-IP: 1.1.1.1"If your login throttling, firewall, or audit log reads those headers, the attacker can rotate a fake IP on every request — defeating rate limits, evading IP bans, and poisoning your logs. This is why a good security plugin warns you before changing the detection method: the "more convenient" setting is often the insecure one.
CF-Connecting-IP is safe from this only because of one extra condition: your origin must accept traffic solely from Cloudflare. If your server is also reachable directly on its public IP, an attacker can skip Cloudflare and forge CF-Connecting-IP themselves. Lock the origin down with Authenticated Origin Pulls or a firewall that allows only Cloudflare's IP ranges, and the header becomes trustworthy.
The right setting, in plain terms
- Plain server, no proxy: use
REMOTE_ADDR. Done. - Behind Cloudflare: use
CF-Connecting-IP(HTTP_CF_CONNECTING_IP), and restrict your origin to Cloudflare so the header can't be spoofed. - Behind another proxy/CDN or a load balancer: use the header that proxy documents (often
X-Forwarded-For, taking the right-most trusted hop), and again make sure the origin only accepts traffic from that proxy.
In WordPress security plugins such as All-In-One Security, this is exactly the choice the "IP detection" setting controls. Leave it on REMOTE_ADDR unless you are behind Cloudflare, in which case switch it to CF-Connecting-IP.
How to verify you got it right
The fastest sanity check is to compare what your server detects against your real public IP. Open whatsmy.fyi from the same machine: it shows the exact public IP the outside world sees you as. If the IP your plugin or app logs for your own visit matches that, detection is working. (whatsmy.fyi runs on Cloudflare itself, so it reads your address from CF-Connecting-IP the same way a correctly-configured origin would — and it stores nothing.)
If you are debugging programmatically, the keyless endpoint returns the same value for scripts:
curl https://whatsmy.fyi/ip
# → your real public IP, as seen at the edgeTwo IPs that match means your detection is correct. If your server logs a Cloudflare IP while whatsmy.fyi shows your real one, you are still reading REMOTE_ADDR behind the proxy — switch to CF-Connecting-IP. If they differ in any other way, a VPN, IPv4/IPv6 split, or a second proxy is in the path — our note on why an IP can show the wrong location covers those cases.
The one-line takeaway
REMOTE_ADDR is the truth your server can't be lied to about; HTTP_CLIENT_IP is a claim anyone can make. Behind Cloudflare, prefer CF-Connecting-IP and lock your origin down so it stays trustworthy — then confirm the result against your real address at whatsmy.fyi.


