Attacks & threats

7 min readUpdated

What is an HTTP flood?

Quick answer

An HTTP flood is a denial-of-service attack carried out with ordinary web requests. Instead of trying to saturate the network, the attacker sends requests that are expensive for the server to answer — a search query, a login attempt, a cart operation — until the application runs out of workers, database connections or CPU. Each request is individually valid, which is what makes it hard: there is no malformed packet to drop and no bandwidth spike to notice.

01Cheap to send, expensive to answer

The asymmetry is the whole attack. A GET request is a few hundred bytes and costs the attacker nothing. Answering it might mean a database query across a large table, rendering a template, and a round trip to a cache — tens of milliseconds of work.

At a few thousand requests per second, the traffic is trivial on the wire and completely overwhelming for the application. This is why an HTTP flood can take down infrastructure that would shrug off a hundred gigabits of junk packets.

It is also why the defences differ. The difference between Layer 3/4 and Layer 7 attacks is not only which layer is hit but which resource runs out, and capacity that absorbs packets does nothing for a database at its connection limit.

Volumetric floodHTTP flood
Measured inGigabits per secondRequests per second
What runs outBandwidthWorkers, DB connections, CPU
Visible on a traffic graphObviouslyOften not at all
Stopped byUpstream capacityRequest inspection
Individually valid requestsNoYes — that is the problem

02The common variants

HTTP floods come in a handful of recurring shapes. They often run together, and the mix tells you what the attacker has learned about your site.

VariantWhat it doesWhat runs out
GET floodRepeats requests for pages that cannot be served from cacheApplication workers and database time
Cache-busting floodAppends a random query string so every request misses the cacheThe origin, which now answers what the cache should have
POST floodSubmits forms, searches or API calls the server must parse and processCPU, request-body buffers, write capacity
Login floodHammers authentication, often mixed with credential stuffingCPU spent on deliberately slow password hashing
Slow requestsSends headers or bodies a byte at a time to hold connections open, as in a Slowloris attackConnection slots rather than CPU
HTTP/2 stream abuseOpens and cancels streams faster than the server can clean up, as in HTTP/2 Rapid ResetRequest handling, at enormous rates per connection

03What attackers aim at

A competent attacker does not hit your homepage, because your homepage is probably cached. They look for the endpoints that cannot be cached and cost the most.

  • Search endpoints — full-text queries across large tables
  • Login — deliberately slow password hashing, by design
  • Cart and checkout — session state, inventory locks, no caching possible
  • Filtered listing pages, where every parameter combination is a fresh query
  • Any URL with a random parameter appended, which defeats caching entirely
  • API endpoints that fan out into several backend calls

04How defenders tell it apart

Since no single request is malformed, detection looks at behaviour across many requests rather than at the content of one. The broad families are common knowledge across the industry; what differs between providers is how they are combined and weighted, which nobody publishes, because a published list is a checklist for attackers.

  • Consistency: whether the client behaves like the software it claims to be; TLS fingerprinting is the best-known industry example
  • Browsing behaviour: real visitors move through a site in varied ways; floods repeat a few expensive URLs
  • Request mix: a sudden shift in which endpoints are being hit, compared with what that site normally sees
  • Distribution: many new sources converging on the same endpoint at the same moment
  • Capability: whether the client completes a challenge that a real browser handles without the visitor noticing, such as a proof-of-work challenge

05Why rate limiting alone is not enough

The instinctive response is a per-address rate limit, and it does help against a single noisy source. Against a distributed attack it does very little: ten thousand bots making two requests a second each stay comfortably under any sane limit while delivering twenty thousand requests per second to your origin.

Rate limiting is still worth having — it stops the trivial cases and puts a ceiling on damage — but it has to be one layer among several rather than the whole defence.

06Find what is being hit

Before changing anything, find out which endpoint the flood is aimed at. The access log answers that in seconds. A path whose number of distinct URLs is close to its number of requests is being cache-busted, and an endpoint that has suddenly become the most requested on the site is the target.

Count the distinct clients as well. A few addresses doing most of the work can be limited individually; thousands of addresses doing a little each cannot, and that tells you the problem needs filtering in front of the server rather than rules on it.

Access-log triage, combined log format (run with GNU Awk 5.2.1 against a test log)
# requests | distinct URLs | path: distinct close to requests means cache-busting
awk '{split($7,u,"?"); t[u[1]]++; if(!seen[$7]++) d[u[1]]++}
     END{for(p in t) print t[p], d[p], p}' access.log | sort -rn | head

# who is hitting the target, and how many distinct clients there are
awk '$7 ~ /^\/search/ {print $1}' access.log | sort | uniq -c | sort -rn | head
awk '$7 ~ /^\/search/ {print $1}' access.log | sort -u | wc -l

# status codes on the login route: a wall of 401s is a login flood
awk '$7 ~ /^\/login/ {print $9}' access.log | sort | uniq -c | sort -rn

07Per-route limits on the origin

Per-route limits on the expensive endpoints are the next step. They will not stop a distributed flood, but they stop one noisy client from occupying a whole worker pool, and they make the expensive paths cheap to refuse. The fuller set of limits, timeouts and caching rules is in nginx DDoS protection.

Answer refused requests with 429 Too Many Requests, the status RFC 6585 defines for rate limiting, rather than with a full error page. nginx returns 503 by default, which tells the client the whole service is unavailable rather than that this client is over its limit.

nginx, inside http { } (checked with nginx -t on nginx 1.22.1; rates are examples, size them from your own logs)
# Behind a proxy or CDN, restore the visitor address first
set_real_ip_from 203.0.113.0/24;      # your provider's published ranges
real_ip_header   X-Forwarded-For;
real_ip_recursive on;

limit_req_zone  $binary_remote_addr zone=search:10m rate=2r/s;
limit_req_zone  $binary_remote_addr zone=login:10m  rate=10r/m;
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_req_status  429;
limit_conn_status 429;

upstream app { server 127.0.0.1:8080; }

server {
    listen 80;
    server_name example.com;
    limit_conn perip 20;

    location /search {
        limit_req zone=search burst=10 nodelay;
        proxy_pass http://app;
    }
    location = /login {
        limit_req zone=login burst=5 nodelay;
        proxy_pass http://app;
    }
    location / {
        proxy_pass http://app;
    }
}

08What works

  • Challenge ambiguous traffic instead of blocking it, so a false positive costs a moment rather than a customer
  • Cache what can be cached — those requests never reach the application at all
  • Rate limit per route, with tight limits on expensive endpoints and loose ones elsewhere
  • Layer the defences — caching, per-route limits, challenges and inspection — rather than relying on any one of them
  • Keep protection always on, so it is already in place when the first request of a flood arrives
  • Make refusals cheap: a short 429 or a cached error page costs far less than the page that was asked for

09Symptoms and first checks

When the site slows down and the network graph looks calm, these are the patterns to check first. To rule out a genuine spike or an unrelated origin fault before blaming a flood, see signs of a DDoS attack.

SymptomLikely causeFirst check
CPU at 100%, bandwidth normalHTTP flood on an expensive endpointTop paths in the access log for the last ten minutes
Cache hit ratio collapsesCache-busting query stringsDistinct URLs per path
Database connection pool exhaustedFlood on search or filtered listingsThe slow-query log and the paths behind it
Many open connections, few completed requestsSlow-request attackConnection states on the web server
Spike of 401 or 403 on the login routeLogin flood or credential stuffingFailed logins per minute and the spread of usernames
Proxy shows 502 or 504, origin looks idleOrigin workers stuck waiting on a slow dependencyWorker counts and upstream timeouts on the origin

Questions

Frequently asked questions

A volumetric attack exhausts bandwidth and is obvious on a graph. An HTTP flood exhausts application resources with a request rate that often looks unremarkable. They need completely different defences.

Partly. Requests for cacheable content are absorbed at the edge, which removes a large share. Requests deliberately aimed at uncacheable endpoints still reach your origin, so you also need inspection.

That is the classic HTTP flood signature. The traffic is small; the work it causes is not. Look at which endpoint is being requested and how the request mix compares to a normal hour.

429 Too Many Requests, which RFC 6585 defines for exactly this case, optionally with a Retry-After header. Some servers default to 503, which says the whole service is unavailable rather than that this client is over its limit.

Usually. Distribution is what makes them work — it defeats per-address limits and makes the traffic look like many different visitors.

Sources

Sources and verification

nginx snippet checked with nginx -t (nginx 1.22.1, Debian package with the realip module) and exercised against a local upstream: a burst above the search limit received 429, while a second client and unlimited paths were unaffected; awk triage commands run with GNU Awk 5.2.1 against a crafted access log. Debian 12, October 2026.

  1. 01Google mitigated the largest DDoS attack to date, peaking above 398 million rps · Google Cloud
  2. 02RFC 6585: Additional HTTP Status Codes (429 Too Many Requests) · IETF
  3. 03Module ngx_http_limit_req_module · nginx.org
  4. 04Module ngx_http_limit_conn_module · nginx.org
  5. 05Module ngx_http_realip_module · nginx.org

Last updated