How Cloudflare Absorbs a DDoS Attack
A distributed denial of service attack is not a clever exploit. It is a flood. The attacker does not need to find a vulnerability in your code. They need to send more traffic than your infrastructure can handle, and then
A distributed denial of service attack is not a clever exploit. It is a flood. The attacker does not need to find a vulnerability in your code. They need to send more traffic than your infrastructure can handle, and then your real users cannot get in. The interesting engineering question is not how to stop the flood at its source, because you cannot. The question is how to build a system that stands in the flood and keeps serving legitimate requests anyway. Cloudflare is the most studied answer to that question, and its design is worth understanding even if you never run a network of its size.
The first idea is anycast, and it is the foundation everything else rests on. In a normal setup, your domain resolves to an IP address that lives on one server in one data center. Every request, legitimate or malicious, goes to the same place. Cloudflare announces the same IP address from more than 300 cities at once. When a user in Karachi requests your site, BGP routing sends them to the nearest Cloudflare data center. When a botnet in another region fires a flood, that traffic is also spread across the nearest data centers to the bots sending it. The attack is not concentrated on one target. It is diluted across a global network before any filtering even begins.
This matters because a DDoS attack is usually a volume problem before it is a filtering problem. A single data center with a 100 gigabit link dies when a 200 gigabit flood arrives, no matter how good its filters are, because the pipe itself is full. A network with hundreds of data centers, each with its own capacity, can take the same flood in pieces. No single location sees enough of it to saturate. The architecture turns one impossible problem into hundreds of manageable ones.
The second idea is that filtering happens at the edge, as close to the attacker as possible. Cloudflare's network runs its own software stack on every server, and that stack inspects traffic in layers. At layer 3 and 4, the network layer, the system deals with the bluntest attacks: SYN floods, UDP floods, and ICMP floods. These attacks try to exhaust connection tables and bandwidth with raw packets. The mitigation here is mostly statistical and structural. A SYN flood is answered with SYN cookies, a technique that lets the server validate a connection attempt without allocating memory for it first. UDP floods aimed at random ports are dropped at the edge because there is no service listening there. The goal at this layer is to discard the obviously invalid traffic with as little work as possible, so the expensive analysis is reserved for traffic that looks almost legitimate.
Layer 7 is where the attack gets interesting, and where the filtering gets smarter. A layer 7 attack is an HTTP flood: millions of requests that look like normal GET requests, hitting expensive endpoints, perhaps with rotating user agents and residential IP addresses. You cannot drop these by IP address alone, because the addresses look real and keep changing. Cloudflare handles this with a combination of rate limiting, behavioral analysis, and challenges.
Rate limiting is the simplest tool and still one of the most effective. A data center tracks how many requests each client makes in a window, and when a client exceeds a threshold, further requests are blocked or slowed. The threshold is set per endpoint, because a login endpoint and a static image have very different legitimate rates. A single global limit would either block real users or let the attack through. The configuration below is the kind of rule a Cloudflare customer writes for a sensitive endpoint, and the same shape works in any reverse proxy.
{
"description": "Limit login attempts per IP",
"match": {
"request": {
"url": { "path": { "contains": "/login" } }
}
},
"action": "block",
"ratelimit": {
"characteristics": ["ip"],
"period": 60,
"requests_per_period": 10,
"mitigation_timeout": 300
}
}
Behavioral analysis adds a second layer. The system builds a picture of what normal traffic looks like for a particular site: which countries it comes from, which user agents are common, how fast a human can click through pages. Traffic that deviates sharply from that picture gets a higher risk score. A sudden burst of requests from a region that never visits your site, all using the same unusual user agent, is suspicious even if no single request breaks a rule. The score decides the response: let it through, slow it down, or present a challenge.
The challenge is the third tool, and it is deliberately annoying to bots and cheap for humans. A managed challenge might ask the browser to run a small JavaScript proof of work, or to prove it can execute code the way a real browser does. A simple script making raw HTTP requests cannot pass. A headless browser can, but it is far more expensive to run at scale, and that cost changes the attacker's economics. This is the deeper principle behind all of it: you do not need to make an attack impossible. You need to make it expensive enough that it stops being worth doing.
A fourth idea is capacity itself, and Cloudflare's approach here is unusual. Most mitigation services charge by the volume of attack traffic they absorb, which means a big attack becomes a big bill for the victim. Cloudflare offers unmetered mitigation, absorbing attacks of any size without charging extra. That is only possible because the network is so large that even a record breaking attack is a small fraction of total capacity. The business model and the architecture reinforce each other: the bigger the network, the cheaper each attack is to absorb, and the cheaper absorption is, the more customers join, and the bigger the network gets.
There is also a product called Magic Transit, which extends the same protection to infrastructure that does not sit behind Cloudflare's proxy. A company announces its own IP ranges through Cloudflare's network, traffic to those addresses is routed through Cloudflare data centers, cleaned, and then forwarded to the origin over a tunnel. The origin never sees the attack traffic at all. This is how a service that is not a website, such as a game server or an API running on its own infrastructure, gets the same edge filtering.
All of this raises a fair question for a team that does not run a global network: what can you actually borrow from this design? More than it seems. The principles scale down well.
First, put a filtering layer in front of your origin. That can be a CDN, a load balancer with rate limiting, or a simple reverse proxy. The origin should never be directly reachable from the public internet. If attackers can find your origin IP, they can bypass every layer you built. Keep the origin IP private, accept traffic only from your proxy, and rotate the address if it ever leaks.
Second, rate limit by endpoint and by client, not by server. The nginx configuration below is a workable starting point for a small service. It defines a zone that tracks requests per IP, then applies a limit to a specific location. A client that exceeds ten requests per second gets a 503 response instead of reaching your application.
http {
limit_req_zone $binary_remote_addr zone=login:10m rate=10r/s;
server {
location /login {
limit_req zone=login burst=5 nodelay;
proxy_pass http://app_backend;
}
}
}
Third, make your expensive endpoints cheap to reject. A login endpoint that runs a password hash on every request is a gift to an attacker, because each request costs you far more than it costs them. Validate the cheap things first: is the request well formed, is the rate acceptable, does the client look like a browser. Only then spend CPU on the expensive check. The same ordering principle applies in code. Here is a token bucket in Go that you can drop in front of a handler, so the limit is enforced before the handler does any real work.
package main
import (
"net/http"
"sync"
"time"
)
type limiter struct {
mu sync.Mutex
tokens float64
lastSeen time.Time
}
var buckets sync.Map
func allow(ip string) bool {
now := time.Now()
v, _ := buckets.LoadOrStore(ip, &limiter{tokens: 10, lastSeen: now})
l := v.(*limiter)
l.mu.Lock()
defer l.mu.Unlock()
elapsed := now.Sub(l.lastSeen).Seconds()
l.tokens += elapsed * 2
if l.tokens > 10 {
l.tokens = 10
}
l.lastSeen = now
if l.tokens < 1 {
return false
}
l.tokens--
return true
}
func rateLimited(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !allow(r.RemoteAddr) {
http.Error(w, "Too many requests", http.StatusTooManyRequests)
return
}
next.ServeHTTP(w, r)
})
}
Fourth, plan for the attack to succeed partially. No filter is perfect. Have a runbook: how do you raise rate limits, how do you block a region temporarily, who do you call at your provider, and what does your status page say. An attack that finds you without a plan costs you hours of improvisation at the worst possible moment.
The lesson of Cloudflare's design is not that you need 300 data centers. It is that denial of service is a capacity and economics problem, and the defense is to push filtering as far from your origin as possible, make each layer of attack expensive to sustain, and never let the attacker's cheapest move reach your most expensive code. Build the filtering at the edge, rate limit by endpoint, reject cheaply, and keep the origin hidden. The flood will still come. The question is whether it reaches you.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.