What we learned from load-testing our own edge to failure

Over the last few months we have spent a lot of time on what we learned from load-testing our own edge to failure. This is what we learned.

Challenges beat blocks

Blocking lists go stale within minutes when attackers rotate through residential proxies. Proof-of-work does not care where a request comes from; it only cares whether the client is willing to pay the cost.

For a real browser that cost is a few hundred milliseconds, once per session. For a botnet sending a million requests a minute it is a million puzzles a minute — and at that point the attack stops being cheap.

Latency budget

Our budget for the whole filtering pipeline is one millisecond at the 99th percentile. Anything that cannot be decided within that budget runs asynchronously and influences the next request from the same client, not the current one.

That constraint shapes everything: data structures, where state lives and which signals we are willing to compute inline.

What we got wrong

Our first version challenged too eagerly on mobile networks, where thousands of real users share a handful of carrier-grade NAT addresses. Reputation that is shared is reputation that is noisy.

We now weight fingerprint consistency and session behaviour far more heavily than IP reputation for traffic from known mobile carrier ranges.

Testing in production, safely

Every rule starts in log mode. We replay the previous seven days of traffic through it and show exactly which requests it would have affected before anyone can promote it to challenge or block.

This one feature has prevented more incidents than any detection we have ever shipped.

Compliance is a side effect

Regulators increasingly ask for evidence of resilience, not just promises. An incident timeline with start, peak, vectors and impact is exactly the evidence DORA and NIS2 ask for — and it falls out of good observability for free.

We export incident reports in formats auditors can file without anyone rewriting them.

What actually happens during a flood

The first thing to fail during a layer 7 flood is almost never bandwidth. It is connection slots, worker processes or database connections on the origin — resources measured in hundreds or thousands, not gigabits. An attacker who can make each request expensive only needs a few thousand requests per second.

That is why we score requests before they are proxied. By the time a request reaches your origin, it has been attributed to a client, compared against that client’s history and weighed against the current load on the route it targets.

The numbers

Across the last quarter, 71% of challenged clients never attempted a solution, 24% solved one challenge and then behaved normally, and 5% solved challenges repeatedly while continuing to attack — the last group is where analysts spend their time.

Median added latency for legitimate visitors that were challenged was 280 ms on desktop and 410 ms on mid-range Android devices.

{ "match": { "path": "/wp-login.php", "risk": ">= 62" }, "action": "challenge" }

We will follow up with the numbers from the next quarter.

9 thoughts on “What we learned from load-testing our own edge to failure”

  1. We moved from a scrubbing provider to always-on last year; time to mitigation went from minutes to basically nothing.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top