Why the block fired in the first place

The Ref number on the blocked page read 104.23.x.x. That’s a Cloudflare edge address. The visitor it belonged to was on a residential VPN in Florida.

The Florida VPN customer had filed a support ticket saying he couldn’t reach the site at all, just a block page with a reference number. Support pulled the Ref number to look him up in the firewall log and got nothing, because the number on his screen wasn’t his IP. It was ours, or more precisely, Cloudflare’s edge server IP for the request that hit our origin.

His VPN egress IP sat inside a hosting-provider range we’d blocklisted months earlier, the kind of range abused for scraping and credential stuffing. The rule was doing its job. Once we found his actual IP through the account and correlated the timestamp, unblocking just that address was the easy part.

The harder part was the Ref number itself, because it’s supposed to be the tool that makes this fast. A customer hits a block, screenshots the Ref number, support pastes it into the firewall log, done. Except the value being captured wasn’t the client IP. It was whatever IP the code found first when it walked the request headers, and on our stack that was landing on the connecting peer address at the edge, i.e. Cloudflare’s own server, instead of CF-Connecting-IP.

The check that builds the block page constructs the record from the request object. Somewhere in that path, someone had grabbed the socket-level remote address as a fallback and it was winning over the header Cloudflare actually sets for this purpose. That fallback was correct enough when no proxy sat between the client and our origin, and wrong every time Cloudflare did.

Same bug, two call sites

Once I saw it in the block page, I checked the shared Cloudflare IP-parsing helper the rest of the app calls, and it had the identical ordering bug. It wasn’t confined to the block page template. Anything using that helper to log or display “the visitor’s IP” was capturing edge infrastructure instead of the actual visitor whenever the two disagreed, which in practice is whenever the header exists at all. It had presumably been silently wrong for a long time; it just took a blocked VPN user with a support ticket for anyone to notice, because the two only diverge when the header path is exercised.

The fix was to make CF-Connecting-IP the read, with the raw remote address only as fallback when the header is absent, not the other way around:

def get_client_ip(request):
cf_ip = request.headers.get("CF-Connecting-IP")
if cf_ip:
return cf_ip
return request.remote_addr

Trivial diff. The bug wasn’t the logic, it was that the fallback had been promoted to primary at some point and nobody had a reason to look until the number on screen stopped matching anything a human could search for.

Verifying it actually mattered

Shipped as a hotfix, then checked live against his session: the block page rendered his real VPN egress IP in the Ref number, not an edge address. Confirmed the shared helper too, since fixing only the block page and leaving the helper wrong would’ve meant the next support ticket hit the exact same wall somewhere else in the app.

Closed the ticket with a reply for support to send him, and the account got its own whitelist entry so the range block doesn’t refire on him specifically. But the real fix wasn’t the whitelist, it was making the Ref number trustworthy again. A block page that shows the wrong IP isn’t just a cosmetic bug, it silently breaks the one support workflow that exists to resolve blocks quickly, and it does it exactly in the cases where you most need the number to be right: when the request came in through a proxy layer instead of a plain client connection.