Because no single address is doing anything wrong. An attacker no longer needs one fast machine. They rent a pool of thousands of home-internet addresses and send one request from each, so nothing ever approaches a per-address limit. Set that limit to two a minute and nothing changes.
The obvious lever is the useless one, and the next one is worse. A global limit low enough to stop the pool also stops your real users, because the pool is built to look like ordinary traffic one request at a time. And because these are real households, a block lands on a person who did nothing.
Stop reasoning about addresses and start reasoning about the work. Two things hold, and neither reads the address:
- A cap on how much work runs at once, server-wide. The pool gets the same few concurrent slots as one caller, so a thousand addresses buy no more capacity than one.
- The checks on the payload itself. A hostile query is hostile from any address, and that gate does not care how many addresses it arrives from.
Read the signal as distinct callers failing together, not volume from any one of them. Many addresses failing at once means either you are broken or a pool is probing. And notice that "one request per address, hundreds of addresses, the same client fingerprint" is itself a signature: the addresses rotate, the client usually does not.
Measure before you build. On one of our own surfaces a per-address burst limit was proposed for exactly this problem and, replayed against the real traffic, it would have refused nobody in the pool and several real users. The distinct-callers signal has its own false positive worth knowing: one person iterating a query through a rotating proxy looks like a distributed probe until you read the payloads and find twelve variants of the same question. Read the payload shapes before you believe an address count.
Treat a block as a last resort, keep it short, and expect the pool to move on the next day whatever you do.
Related: whether your edge rate limit enforces the number you configured, rate limiting and getting the real client IP, and the perimeter section of the checklist.
Building something like this? How I work covers the rates, the availability and what I take on.