Search “Singapore bot traffic” and you’ll land on a Google Analytics community thread that’s become a rite of passage: a site owner watching GA4’s real-time report fill up with hundreds of “users” from Singapore, out of nowhere, and asking the two questions everyone asks next — is this ghost spam, or an actual bot attack, and either way, how do I make it stop?

That distinction matters more than it looks. Ghost spam never touches your site at all — it’s a fake hit fired straight at your GA measurement ID, so your real server or CDN logs show nothing unusual, and no firewall rule will fix it (you need a GA-side hostname filter instead). Real bot traffic actually requests your pages, which means it shows up in your own logs too — and that’s the case a firewall can do something about.

We spent a full investigation cycle on a WordPress site that had the second kind, and the fix that actually worked wasn’t the one guides usually recommend.

The advice everyone gives

Articles on this topic — this one from webnots is representative — point out (correctly) that Singapore hosts a huge concentration of cloud infrastructure, so it’s a natural source of crawler and scraper traffic. The recommended fix is almost always some form of country block: a Cloudflare rule, a WAF rule, or a hosting-panel geo-block, usually built from a standard “IP ranges by country” list.

We did exactly that. It was the right first move, and it worked — the firewall counters showed real packets being dropped, thousands of them, every day.

Singapore kept showing up in GA at close to the same volume anyway.

The traffic that should have been blocked, wasn’t

The instinct at that point is to assume the block is misconfigured. It wasn’t. The actual explanation only shows up if you stop trusting either data source at face value and go straight to the logs.

The key fact: analytics only records requests that actually load. A request a firewall drops never reaches WordPress, never fires the tracking script, and never becomes a GA session. So whatever GA is counting as “Singapore,” by definition, is traffic that is currently getting through every rule in place — the real question isn’t “why isn’t the block working,” it’s “which specific IPs is GA calling Singapore, and why aren’t they in the block’s data set.”

Pulling a broad, unbiased sample of live 200-response IPs and geolocating them answered it. Only a handful were Singapore — and every one of them belonged to the same network operator, Zenlayer (AS4229), a network that runs edge infrastructure commercially geolocated as Singapore by IP-intelligence services, but whose address blocks aren’t registered as Singapore in the official regional registry data that most “country IP list” downloads (ipdeny.com and similar) are built from.

That’s the actual gap: a country block built from RIR delegation records and a GA geolocation label built from commercial IP-intelligence data can legitimately disagree about the same address. The block wasn’t broken. It was accurate to a data source that simply didn’t cover this network.

Once the real ASN was identified, the fix was to stop blocking by country label and start blocking by the network’s own announced routes instead — a lookup against that ASN’s route table, applied directly. That traffic stopped immediately and stayed stopped, verified against the live logs.

The broader lesson

“Singapore” in your analytics dashboard is usually telling you which cloud or edge network happened to host a request, not which country an operator sat in. Two different classification systems — regional registry data and commercial geolocation — don’t always agree on where a given block of addresses “is,” and a country block only ever inherits the limits of whichever list built it.

The durable fix isn’t a bigger or more frequently-updated country list. It’s the same loop every time: don’t trust the label, sample the real traffic still getting through, identify the actual network it’s coming from, and block that network directly. It’s slower than flipping on a “block Singapore” toggle — but it’s the version that keeps working after the toggle stops.

This is the same investigation that led to Lubber — more of what came out of it in future posts.