IP & Networking

REMOTE_ADDR vs CF-Connecting-IP: Getting the Real Visitor IP Behind Cloudflare

If your WordPress security plugin or PHP app logs the wrong IP behind Cloudflare, here is why — and how to read the real visitor IP safely using CF-Connecting-IP instead of REMOTE_ADDR or the spoofable HTTP_CLIENT_IP header.

6 min read·

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_IP and HTTP_X_FORWARDED_FOR — ordinary HTTP request headers. Any HTTP_* entry in $_SERVER is just a header the client sent, which means anyone can set it to anything with a single line of curl.
  • HTTP_CF_CONNECTING_IP — the CF-Connecting-IP header 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 edge

Two 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.

Check your IP address, location, and privacy score — instantly.

Zero logs. Zero tracking. Zero external APIs.

Run the check now →

Related articles