The site was slow, CPU was pinned, and our DDoS playbook had a clear first move: rank the IPs by hit count and block the loudest. We ran it, looked at the top of the list, and found nothing worth blocking. The attacker was 55-plus unique IPs, each hitting a single page once or twice, all from one hosting ASN. The top-by-volume ranking could not see a single one of them.

While the site was slow and CPU was pinned, the ranking kept pointing at nothing. That was the real cost of the gap.

What the ranking was built for

Our standing tool walked the IIS log, grouped by source IP, sorted by hit count, and surfaced the top of the list. It works, and it has caught real bots. Earlier in this same incident it flagged AwarioBot, a brand-monitoring crawler, at 1,643 hits from a single Hetzner IP. One noisy IP, one obvious line at the top, one cheap fix: a single user-agent block. That is the shape top-by-volume is built for, a concentrated bot.

“Concentrated” is an assumption baked into the ranking. When we ran the same analysis against the swarm, the top of the list was full of legitimate traffic and recognizable crawlers. Nothing at the top was the cause. The cause sat in the long tail, where every IP looked like a single curious visitor.

What the swarm looked like

The signature was the inverse of what the tool ranks for. A single target URL, a page listing members of a group with query permutations, was being hit from a pool of 55-plus distinct source IPs. Each IP was responsible for only one to three requests. Sorted by hit count, none of them cracked the list. Sorted by anything per-IP, they were noise.

What they shared was origin. Nearly all of them traced back to the same hosting ASN, Alibaba Cloud, inside a documented hosting range for that provider. The signal was how many distinct IPs were aimed at the same page from the same network.

Changing the primary grouping

For a suspected swarm, the analysis becomes:

  • group by URL plus query string,
  • rank each URL by its count of unique source IPs,
  • score each by what fraction of those IPs fall in known-bad ranges.

Under top-by-volume, a page hammered by one IP and a page fanned out across 55 IPs look identical if the totals match. They are different attacks. Unique-IP-count per URL separates them immediately. The member-listing page lighting up with dozens of one-shot IPs is a swarm. The same page hit hard by three IPs is something else.

The bad-range percentage is the second axis, and it keeps you from overreacting. Event days produce real traffic spikes, and a page can show a high unique-IP-count for legitimate reasons. Scoring each suspect URL by the share of its source IPs already in flagged ranges lets you call swarm, mixed or organic without blocking real customers on the busiest day of their season. The verdict is a function of two numbers, bad-percentage and unique-IP-count.

The block

Once the swarm was identified by its ASN rather than by any individual IP, the mitigation was a single Windows Firewall rule against the Alibaba Cloud range:

New-NetFirewallRule -Direction Inbound -Action Block -RemoteAddress <hosting-range> ...

CPU dropped from 73% to 11% and current connections from roughly 1000 to 128, in seconds. A TCP-dropped packet at the firewall costs the IIS layer nothing, whereas an app-layer block still runs the request through the worker process to issue a redirect. For a distributed swarm from a single network, the firewall is both the right layer and the cheap one.

It rotated

Blocking the range did not end it. Within hours the swarm rotated to a different documented /10 inside the same ASN. Alibaba Cloud owns far more address space than our IP-ranges data had ever flagged, so our internal list was a stale subset of the real allocation. Block from the actual public whois allocations (ARIN, RIPE, APNIC), not from your own historical data.

The same broader space also includes carrier mobile ranges (Bell Mobility Canada, AT&T Mobility). A lazy wide block across it would have taken out real customers on their phones. ASN-level blocking is a moving target in both directions: the attacker rotates into ranges you do not know about, and the ASN owns ranges you must never touch. So every rule got a dated, named convention, which means querying the firewall rules reconstructs the block history later and ties each block back to an incident.

Matching the tier to the attacker

Picking the wrong tier is how you either miss the attack or block real users:

  • A single named crawler hammering every sort permutation of a page (AwarioBot was the example) gets one line in the user-agent blocklist. It is identified and not rotating.
  • A single aggressive IP gets an IP-level block.
  • A distributed swarm gets a firewall CIDR block scoped to the ASN, because the user-agent is generic or rotating and the per-IP volume is too low to catch.

Top-by-volume covers the first two. It is structurally blind to the third, and the third is the one that pinned the box.

If CPU spikes and the top-IP list looks clean, group by full URL plus query string and rank each by unique source IPs. Check what share sit in a known-bad range before you call it a swarm, and before you write any CIDR block, confirm the range does not overlap a carrier or mobile allocation.