I’ve had real vs. bot vs. scan tracking running since September 7 (see the live counter at /visits). Enough has come in now to say something about it.
Correction, later the same day: this post originally said the top “scanning” IP had spent 546 requests “hammering” this site’s private admin page — described like an external actor. It wasn’t. That IP turned out to be this project’s own Claude Code development environment: rate-limit testing, deploy verification, and CSP checks, repeatedly hitting real pages that happen to exist on this exact site. A classifier bug (explained below) had mislabeled all of it as vulnerability scanning. The numbers below are corrected; the full story of how this got caught is in Claude’s Notes.
Since tracking started, this site has logged 1,733 requests that weren’t Lightsail’s own health checks. Of those:
So for every real visitor, there were roughly four-and-a-half scan probes — not far from what the original (wrong) numbers said, coincidentally, since the correction mostly moved requests out of “scan” and into a new bucket rather than into “real.”
What the actual concentration looks like now: all 870 scan hits came from 13 IPs, and three of them account for almost all of it — all three on Google Cloud IP ranges, each making 277 requests with an identical spoofed Chrome user-agent. Probably the same tool or operator running three instances in parallel.
That UA-spoofing is exactly why this site’s scan detection checks the path first and the user-agent second: a scanner claiming to be an ordinary browser is still obviously a scanner if it’s asking for /wp-admin or /.env.backup on a static Astro site with no backend. The long tail makes that clear — dozens of one- or two-hit probes for .env, .env.bak, .env.staging, .git/config, and similar credential-filename guesses. Classic wordlist scanning: spray the same list of “might be an exposed secret” paths at every host on the internet and see what answers. None of it finds anything here, because there’s nothing to find — no backend, no .env served, no database. But the scanners don’t know that going in, and they’re not really targeting this site specifically, just trying it like they try everyone else.
None of this changes the plan from a couple weeks ago: it’s still not worth putting Cloudflare or a WAF in front of a static site on a nano instance just to swat noise that isn’t costing much. But it’s a good reminder of why the real/bot/scan split exists in the first place — without it, that 50% would look like a much bigger, scarier number than it is, and for a few days it also looked like a much bigger number than it actually was for a much sillier reason.