Rate Limiting, Quotas & DDoS Defense
Split defenses into L3/L4 volumetric (anycast, CDN/scrubbing, SYN cookies, BGP) and L7 application (WAF, behavioral limits, graduated challenges); rate-limit with token bucket on multiple dimensions and tiered quotas in Redis; return 429 with Retry-After; decide fail-open vs fail-closed per endpoint; and cap autoscaling so you do not denial-of-wallet yourself.
DDoS is at least two problems at two layers
The mistake juniors make is treating "DDoS" as one problem with one fix. It is at least two problems that live at different layers and need different defenses.
L3/L4 volumetric attacks
These try to saturate your pipes or your connection tables: UDP reflection/amplification (DNS, NTP, memcached, giving 50x to 50000x amplification), SYN floods, ACK floods. Measured in Gbps and Mpps (millions of packets per second), a large one is hundreds of Gbps to multiple Tbps. You cannot absorb that on your origin. The defense is upstream and distributed: anycast advertises the same IP from hundreds of edge PoPs so an attack is split across the whole global network instead of hitting one datacenter, a CDN/scrubbing center (Cloudflare, AWS Shield Advanced, Akamai) filters malformed and reflected packets before they reach you, and for on-prem you can use BGP flowspec or a scrubbing provider to divert and clean traffic. SYN floods are handled with SYN cookies so no state is allocated until the handshake completes.
L7 application floods
The sneaky ones: valid-looking HTTP requests that each cost you a database query or an expensive render. A few thousand well-chosen requests per second to a search endpoint can take you down while looking like normal traffic at the network layer. Here you need a WAF (rule and signature matching, OWASP core ruleset), behavioral rate limits per identity, IP reputation and ASN blocking, and a graduated challenge: suspicious clients get a JS challenge or a managed CAPTCHA, and truly abusive ones get proof-of-work (make the client burn CPU before you spend a query). The graduated response matters because you do not want to CAPTCHA your real users.
Sort each defense by the attack layer it actually addresses.
Rate-limiting algorithms
token bucket : refill R tokens/sec, capacity B; allows bursts up to B, smooths to R
sliding window : count requests in the trailing T seconds; accurate, more memory
fixed window : count per calendar minute; cheap but allows 2x burst at the boundary
Token bucket is the usual default (bursty but bounded). Apply limits on multiple dimensions: per API key, per user, per IP, per endpoint, and offer tiered quotas (free 100 req/min, pro 10k req/min). Store counters in Redis with atomic Lua scripts so the check is one round trip.
Fail-open vs fail-closed
If your Redis limiter store is down, do you allow all traffic (fail-open, availability first, risk letting an attack through) or block all traffic (fail-closed, safety first, risk a self-inflicted outage from a Redis blip)? For a general public API you usually fail-open with a conservative local fallback limit so a limiter outage does not become a total outage; for a login or payment endpoint you fail-closed because letting abuse through is worse.
Interview nuance: name economic denial-of-service (denial of wallet). If your response to load is to autoscale, an attacker who cannot take you down can still make you spend: they drive traffic, you scale to 500 instances, and the bill (or your serverless invocation count) explodes. Cap autoscaling, put a cache/CDN in front to shed load cheaply, and set billing alarms.
Recap: split defenses into L3/L4 volumetric (anycast, CDN/scrubbing, SYN cookies, BGP) and L7 application (WAF, behavioral limits, graduated challenges); rate-limit with token bucket on multiple dimensions and tiered quotas in Redis; return 429 with Retry-After; decide fail-open vs fail-closed per endpoint; and cap autoscaling so you do not denial-of-wallet yourself.
Apply
Your turn
The task this lesson builds to.
Design DDoS protection plus abuse rate limiting for a high-traffic public API covering volumetric floods and L7 HTTP floods.
Think about
- How do L3/L4 volumetric and L7 application defenses differ?
- What is economic denial-of-service (denial of wallet)?
- What is the fail-open vs fail-closed decision for the limiter?
Practice
Make it stick
A second problem on the same idea, so it survives past today.
Design abuse defense for a serverless GraphQL API (AWS Lambda + API Gateway) powering a startup's mobile app at 5k QPS, where a single crafted GraphQL query can fan out into hundreds of resolver calls and every invocation costs money. Lead with how you stop query-cost abuse and denial-of-wallet on a pay-per-invocation stack.
Think about
- Why does a plain QPS limit fail to stop GraphQL cost abuse?
- How do query depth/complexity limits and persisted queries close the surface?
- What serverless-specific controls cap the denial-of-wallet risk?