There are two moments when people learn how DDoS mitigation actually works. The first is calm, at purchase time, reading a feature list that says “DDoS protection included” and quietly assuming the sentence covers everything. The second is at three in the morning, when the site is down, the graphs look wrong in a way that makes no sense, and that included protection is — correctly, and by design — doing nothing at all.
Both moments involve the same product and the same truth: a hosting provider filters the attacks that arrive as raw volume, because it owns the pipe those packets travel down and you do not. It cannot filter the attacks that arrive as ordinary-looking requests, because from the network’s point of view they are ordinary-looking requests. That line — between the flood your host absorbs and the flood you have to survive yourself — is the whole subject. Everything below is about finding which side of it you are on, and what to do on each.
Two different attacks that share one name
“DDoS” is one word covering two problems that have almost nothing in common except the outcome. They are stopped in different places, by different people, with different tools, and confusing them is why so much mitigation effort lands in the wrong layer.
| Volumetric — layers 3 and 4 | Application — layer 7 | |
|---|---|---|
| What arrives | SYN floods, UDP amplification via open DNS, NTP or memcached reflectors, ACK floods, plain garbage packets | Ordinary HTTP requests: GET floods, POST floods, slow-loris, cache-busting query strings |
| Measured in | Gigabits and millions of packets per second | Requests per second — often only a few thousand |
| Bandwidth needed to hurt you | Enormous. This is a capacity contest | Almost none. A single laptop can do it if the endpoint is expensive enough |
| Where it must be stopped | Upstream, by your provider. By the time the packets reach your port, the damage is done | On your server, by you, or on a proxy you control in front of it |
| What it looks like on the box | Interface saturated, packet counters absurd, CPU may be idle | Modest bandwidth, but every worker busy, load climbing, database queue growing |
| Who fixes it | Your host’s scrubbing, automatically, usually within seconds | Your configuration — rate limits, caching, connection ceilings |
Read the last two rows again, because they contain the practical point. If the interface is saturated, nothing you type into the server will help: the packets have already consumed the port, and the only party who can drop them is the one who owns the router upstream. If the interface is quiet but the site is still down, the opposite is true — your host sees nothing wrong because, at their layer, nothing is wrong, and the fix is entirely yours.

What “DDoS protection included” actually buys
Network-layer protection is real, valuable, and almost always misread. When a provider advertises L3/L4 filtering, they mean their network monitors traffic destined for your address, and when a flood is detected the traffic is diverted through scrubbing hardware that drops the malicious portion and forwards what looks legitimate. It happens without a support ticket and usually without you noticing more than a brief blip.
That single sentence is doing a lot of work, so it is worth unpacking what it does and does not include:
- It covers the attacks you cannot survive alone. A 200 Gbps amplification flood against a server with a 1 Gbps port is not a configuration problem. It is arithmetic. Upstream scrubbing is the only answer that exists.
- It is stateless about your application. The filter does not know which of your URLs are expensive, which visitors are logged in, or that a request to your search endpoint costs four hundred times a request to your logo.
- It reacts to a threshold, not to your suffering. Detection triggers on traffic volume. An attack that never crosses the threshold never triggers it, no matter how comprehensively it has taken your site down.
- It may briefly null-route in extreme cases. Every network has a ceiling. If an attack threatens shared infrastructure, the address may be dropped for a period — this is standard, universal, and worth knowing about before it happens rather than during.
The one-line version: your host protects its network, and you benefit from that. It does not protect your application, and it has no way to. Layer 7 is not an upsell that was withheld from you — it is a layer your provider cannot see into without terminating your TLS, which for anyone hosting offshore is a trade with its own serious costs.
First, decide whether this is even an attack
An impressive share of suspected DDoS incidents are something else wearing its coat, and the fixes are not interchangeable. Before rate-limiting anything, spend two minutes ruling out the impostors — a misdiagnosis here costs you an hour and, occasionally, your real users.
- You got popular. A link on a large aggregator produces a traffic shape that looks exactly like an L7 flood, except the referrers are real and the requests are for pages a human would want. This is a capacity problem with a happy cause; rate-limiting it is self-harm.
- A crawler lost its manners. Aggressive scrapers and AI training bots can trivially outrun a small server. The user-agent usually confesses, and the fix is
robots.txtplus a targeted limit, not a general one. - You broke something. A deployment that disabled caching, a runaway cron job, a database that lost an index — all present as “sudden load, no obvious cause”. If the timing lines up with a change you made, believe the change.
- Your own monitoring is the flood. Rare, embarrassing, and much more common than anyone admits. A health-check loop that retries without a backoff can generate a genuinely impressive request rate.
The distinguishing question is simple: does the traffic want something? Real load — even hostile-looking real load — has a shape. It hits pages that exist, follows links, loads assets, and comes from a plausible spread of networks. An attack usually does not bother.
Reading the attack from the server itself
You do not need a dashboard to classify what is happening. Four commands, run in order, will tell you which layer you are fighting in under a minute — and knowing that determines everything you do next.
Is the pipe full? Watch the interface counters. If throughput is pinned near the port’s ceiling, you are in a volumetric attack and your work is a support ticket, not a config change:
vnstat -tr 10— average throughput over ten seconds, the fastest honest read on saturation.cat /proc/net/devtwice, a second apart — packet and byte deltas per interface, no tooling required.
Is it a SYN flood? Half-open connections pile up in SYN-RECV. A handful is normal; thousands is not:
ss -s— the summary line, connection counts by state at a glance.ss -tn state syn-recv | wc -l— the specific number that matters.
Is it layer 7? If bandwidth is unremarkable but everything is slow, count requests per client in your access log. A single address with tens of thousands of hits is an amateur; a hundred thousand addresses with three hits each is the real thing:
tail -n 100000 access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -30- Swap
$1for$7to rank the requested paths instead. If one expensive endpoint dominates, you have found the target and half the fix.
What is actually exhausted? Load average tells you little on its own. Look for the specific ceiling you have hit: PHP-FPM children all busy, database connections at their limit, file descriptors exhausted, or workers stuck in D state waiting on disk. That ceiling — not the traffic — is what took the site down, and raising it is often faster than filtering anything.
Keep this in mind while you look: if you run a privacy-focused service, an attack is precisely when you are most tempted to turn logging up and keep it there. Turn it up if you must, then turn it back down and rotate the files aggressively afterwards. An incident that leaves a month of full-detail visitor logs on disk has traded one problem for a worse and longer-lived one. Our server OpSec guide covers the discipline this belongs to.
The first ten minutes
Under pressure, people reach for the biggest lever available, and the biggest lever is usually wrong. This is the order that limits the damage, roughly fastest-and-safest first:
- Classify before acting. Saturated interface means volumetric; quiet interface with busy workers means layer 7. Thirty seconds spent here saves you from fixing the wrong layer for an hour.
- If it is volumetric, open a ticket immediately and include the destination address, the start time, and your interface counters. Then stop typing into the server — you cannot fix this from inside it.
- If it is layer 7, cache first. Turning on aggressive full-page caching for anonymous visitors is the single fastest way to convert an outage into a shrug, and it is the only measure that helps against real traffic too.
- Then rate-limit — the targeted endpoint first, everything else after. Start conservative. A limit that takes out your own users is a self-inflicted continuation of the attack.
- Block only what is unambiguous. A dozen addresses with a hundred thousand requests each, an obviously fake user-agent, one country you have no users in. Resist the urge to write elaborate rules while under fire; you will not remember them next month.
- Shed load deliberately if you must. Serving a static “under maintenance” page to unauthenticated visitors keeps the box alive, keeps your API up, and buys thinking time. Deciding what to sacrifice is better than having it decided for you.
- Write down what you did. Every temporary rule you added is a landmine for future-you. The rules that helped become permanent; the rest come out tomorrow.
Rate limits that hold, and the mistake everyone makes
Rate limiting is the main instrument for layer 7, and nginx does it well with two directives that solve two genuinely different problems. limit_req caps request rate — how often a client may ask. limit_conn caps concurrency — how many connections one client may hold open at once. Floods need the first; slow-loris attacks, which hold thousands of near-idle connections to exhaust your worker pool, need the second. Deploy only one and you are covered against half the problem.
Three details separate a limit that works from one that is decorative:
- Use a burst, and use
nodelay. Real browsers are bursty — a single page view fires a dozen near-simultaneous asset requests. A limit without headroom throttles genuine visitors while an attacker pacing itself just under the threshold sails through. - Limit the expensive endpoints separately. Your search page, login form, password reset and any endpoint that writes to the database deserve a much tighter budget than your static assets. Attackers find these without trying, because they are the ones that hurt.
- Return 429, not 503. The status code is a signal to well-behaved clients and to search engines that this is throttling rather than failure — and it keeps a bad afternoon from turning into a ranking problem.
The mistake that silently voids all of it: if anything sits in front of your server — a CDN, a load balancer, your own reverse proxy — then every request arrives from its address, not the visitor’s. A per-client rate limit then counts the whole internet as one client, and it will either do nothing at all or ban your entire audience at once. You must configure your real-IP source (in nginx, set_real_ip_from for the proxy’s ranges plus real_ip_header for the header it sends) before the limits mean anything. Restrict it to the proxy’s own ranges, too: trusting a client-supplied header from the open internet lets an attacker forge a new identity per request and walk straight through every limit you own.
Caching is the cheapest mitigation you will ever deploy
A rate limit rejects work. A cache makes the work not exist. For anything an anonymous visitor sees, full-page caching changes the economics of the entire attack: a request that would have cost a database round-trip, a template render and a PHP worker becomes a file read measured in microseconds. The same server that collapsed at four hundred dynamic requests per second will serve tens of thousands of cached ones without noticing.
The things that matter when you turn it on in anger:
- Cache for anonymous visitors only. Bypass on a session cookie. Serving one logged-in user’s page to another is a far worse incident than the outage you were fixing.
- Serve stale on purpose. nginx’s
proxy_cache_use_stalewithupdating error timeoutmeans that when your backend is struggling, visitors get a slightly old page instead of an error. During an attack this is the difference between a site that looks fine and one that looks dead. - Collapse duplicate misses.
proxy_cache_lockensures that a thousand simultaneous requests for the same uncached page produce one backend request, not a thousand. Without it, a cache-busting attack passes straight through the cache and lands on your database at full force. - Neutralise cache-busting query strings. The standard trick is appending
?and a random value so every request is a unique key and misses forever. Normalise your cache key to ignore query parameters your application does not actually use.
There is a pleasant asymmetry here that is worth internalising: every hour you spend on caching also makes the site faster on its best day, cheaper to run, and better at surviving success. Almost no other line of defence pays a dividend when nothing is wrong.
The ceilings that decide whether you fall over
Most servers do not die because they ran out of CPU. They die because they hit an invisible ceiling nobody set deliberately — a default from a decade ago that made sense on hardware nobody uses any more. Under attack, these are what actually break first:
- Application workers. PHP-FPM’s
pm.max_children, your Python worker count, your Node cluster size. This is the real concurrency limit of your site. When they are all busy, every additional visitor queues, and the site is down regardless of how idle the CPU looks. Raise it only as far as memory allows — swapping is worse than queueing. - The accept queue.
net.core.somaxconnand your listen backlog decide how many connections may wait to be accepted. A small backlog turns a survivable burst into refused connections. - SYN cookies.
net.ipv4.tcp_syncookieslets the kernel answer a SYN flood without allocating state for connections that will never complete. Modern kernels enable it by default; verify rather than assume, because it costs nothing and saves you from the most common flood there is. - File descriptors. Every connection is a descriptor. The default
nofilelimit is frequently lower than the number of connections you are trying to serve, and the failure mode — accepts failing while everything looks healthy — is genuinely confusing at 3am. - Database connections. Raising your worker count without raising the connection pool just moves the queue somewhere harder to see. These two numbers must be adjusted together.
Tune these on a calm day, not during an incident. The point of knowing them is that when the site falls over, you can name the ceiling it hit instead of guessing — and a named ceiling is a fixed one.
Putting something in front of the origin
Everything above happens on the server. The next step is deciding whether the server should be the thing receiving the traffic at all. There are three honest options, and the right answer depends far more on what you host than on what you can afford.
| Approach | What it gives you | What it costs you |
|---|---|---|
| Origin directly exposed, hardened | Simplicity, no third party, no TLS termination you do not control | Your address is public and permanent. Layer 7 is entirely on you |
| Commercial CDN or scrubbing service | Enormous absorption capacity, a challenge page in one click, global caching | An abuse desk with an opinion about your content, and a company that can see your traffic. For offshore or DMCA-sensitive projects this can be the weakest link in an otherwise careful setup |
| Your own front node — a small VPS running nginx, proxying to a firewalled origin | Full control, no third party in the request path, an address you can burn and replace, and a real address that stays hidden | Its own capacity limits, and one more machine to run. Two or three fronts in different networks make it meaningfully harder to take down |
The self-hosted front node deserves more attention than it usually gets, particularly for anyone who chose offshore hosting for reasons a large CDN might not sympathise with. The pattern is unglamorous: cheap proxy nodes in front, origin firewalled to accept connections only from those nodes, DNS pointing at the fronts. If a front is attacked, you replace it with a new address in minutes and the origin never noticed. The full version of this architecture — including the six ways an origin address leaks anyway — is the subject of our guide on hiding your origin server IP.
A hidden origin is worth more than any filter
It is worth stating bluntly, because it inverts the usual priority: the cheapest DDoS mitigation available to you is an address the attacker does not have. Filtering is what you do when that has already failed.
This matters more than it sounds, because origin addresses leak constantly and quietly. Historical DNS records from before you put a proxy in front outlive the change by years. Mail sent directly from the application carries the address in its headers. A TLS certificate issued on the raw address is published permanently in certificate transparency logs. An error page, a redirect, or an obscure subdomain that was never proxied all give it away. If you have put a CDN in front of a server that used to be exposed, assume the old address is known until you have changed it.
The corollary is a firewall rule, and it is the most valuable single line in this guide: once something sits in front, the origin should refuse connections on 80 and 443 from everything except that front’s addresses. Without it, the proxy is a suggestion — anyone who learns the real address simply steps around it and attacks you directly, and everything you configured at the front becomes decorative.
Choosing hardware and location so attacks stay boring
Some of this is decided before an attack ever happens, at the moment you pick a plan. Three properties matter far more than the specification sheet suggests:
- Unmetered bandwidth. On a metered plan, an attack is not only an outage — it is an invoice. Traffic you never asked for and could not refuse still counts against an allowance. Unmetered transfer converts a financial risk into a purely technical one, which is a much better class of problem.
- Whether the port is yours. On a shared virtualised host, a neighbour under attack can degrade you, and your own protection ceiling is shared. Dedicated hardware with its own port removes both effects. For a project that expects hostile attention, this is the clearest reason to move up from a VPS — more than cores or RAM.
- Where the network sits. A well-connected European network with real transit capacity absorbs a flood that a poorly-peered one will not, and the jurisdiction you chose for legal reasons has network characteristics too. It is worth checking both when picking from the available locations.
There is also a scale argument that quietly favours simplicity. A static site behind a cache on a modest server is extraordinarily hard to take down; the same content on a heavyweight CMS with an uncached search endpoint can be broken by one determined person with a script. Reducing what is dynamic is a mitigation, and it is free. If your project genuinely lives under sustained load, our high-traffic hosting notes cover the sizing side of the same question.
Five things not to do
The failure modes here are consistent enough to list, and each one has cost somebody a weekend:
- Do not null-route yourself. Blackholing your own address ends the attack in the most literal way possible — nobody can reach you, including your users. It is a tool of last resort for your provider, not an action you take voluntarily.
- Do not treat fail2ban as DDoS protection. It is a fine tool against brute-force attempts from a few addresses. Against a distributed flood it reacts in minutes to something that arrives in seconds, and a rule that bans thousands of addresses can cost more in firewall processing than the attack cost you.
- Do not pay a ransom. The overwhelming majority of extortion emails threatening a devastating attack come from people with no capability who send thousands of identical messages. The small minority who can follow through will come back, because you have proven you pay.
- Do not retaliate. Beyond being illegal essentially everywhere, the sources are compromised third parties. You would be attacking victims, and doing it from an address that is unambiguously yours.
- Do not migrate in a panic. Moving hosts mid-attack means the new address is public within minutes and you have no working configuration. Stabilise first, move deliberately later — and if you do move, our guide on migrating without downtime exists precisely so that the move is not a second incident.
The short version
Stripped of the reasoning, the working model fits in eight lines:
- Classify first. Saturated interface means volumetric and belongs to your host. Quiet interface with exhausted workers means layer 7 and belongs to you.
- For volumetric, file a ticket with the address, the timestamp and your counters — then stop touching the server.
- Cache aggressively for anonymous visitors, serve stale under stress, and collapse duplicate misses. This is the highest-leverage change you can make.
- Rate-limit by request rate and by concurrency, tightest on the endpoints that cost the most, and return 429.
- Fix your real-IP configuration before any of it, or every per-client limit behind a proxy is either useless or catastrophic.
- Know your ceilings — workers, backlog, descriptors, database connections — and raise them deliberately on a calm day.
- Keep the origin address secret and firewalled to your front nodes. This is worth more than every filter combined.
- Buy unmetered bandwidth so that traffic you did not ask for is never also a bill.
None of this makes you immune, and anyone selling immunity is selling something. What it does is move you out of the population that gets taken offline by a bored teenager and into the one that requires genuine resources and genuine intent to disturb — which, for the overwhelming majority of projects, is indistinguishable from safe. The rest is the same unglamorous work that makes a server good at everything else: hardened on the first day, restorable on the worst one, and running somewhere that treats your traffic as your business.