← Back to blog

Layer 7 DDoS Protection: When an HTTP Flood Needs More Than Rate Limits

Maria Ilinca Bostan

Layer 7 DDoS protection filters floods of HTTP requests that look like real visitors, traffic that packet level filtering lets through. Most business web apps can absorb a modest HTTP flood themselves with caching, rate limits and timeouts. Upstream filtering pays off when the flood comes from thousands of IPs at human rates and downtime is expensive.

At voxa.host every VPS and dedicated server includes 100 Gbps of always on protection at Layer 3 and Layer 4. Layer 7 filtering comes only with the 10 Tbps + Layer 7 tier.

Layer 3 vs Layer 7 DDoS: what each attack exhausts

Network attacks at Layer 3 and Layer 4 go after the pipe and the connection tables in front of your server. An application layer DDoS attack goes after the work your application does for each request.

L3/L4 attackLayer 7 attack
Typical formsUDP reflection and amplification (DNS, NTP, memcached), SYN floodsHTTP GET and POST floods, slow request attacks
What it exhaustsLink capacity, packet processing, connection stateApplication CPU, database connections, worker processes
What the traffic looks likeUnsolicited, malformed or far out of proportionValid TCP connections carrying valid HTTP requests
Where it is stoppedUpstream, before it reaches your portAt or in front of the application, by something that understands HTTP
On voxa.host100 Gbps always on included, larger tiers optionalYour own stack on every tier, upstream only on 10 Tbps + Layer 7

Why an HTTP flood can be small in Gbps

Here is the arithmetic, as an example. An HTTP GET with browser headers is around 800 bytes, so 5,000 requests per second is about 4 MB per second inbound, or 32 Mbps. That fits inside the smallest VPS port.

Point those requests at a search page that costs 40 ms of CPU each and you need 200 cores running flat out. A Dual EPYC 9554 has 128 cores. The site falls over while the network sits almost idle.

Why network capacity alone does not stop it

To a network filter, each request is a completed TCP handshake and a well formed HTTP request from a real address. Over HTTPS the URL and headers stay encrypted until your server decrypts them. Nothing about the packets is wrong, so 100 Gbps or 10 Tbps of L3/L4 capacity does not help against an HTTP flood. The traffic is not too big. It is too expensive.

What does an HTTP flood attack look like in your logs?

The pattern is the opposite of a volumetric attack: the port looks normal and the application is drowning.

  • Request rate well above your normal peak, often at an odd hour for your audience.
  • Modest bandwidth. iftop shows tens of Mbps, not a full link.
  • Saturated CPU, database or workers, then 502 and 504 errors from the proxy.
  • Many addresses, each sending a little, with plausible browser user agents.
  • Expensive URLs. Search with random query strings, login, add to cart, checkout, and pages with random parameters that force a cache miss.

Two checks against an nginx access log show the shape:

# requests per client IP, busiest first
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -20

# requests per path, query string removed
awk '{split($7, p, "?"); print p[1]}' access.log | sort | uniq -c | sort -rn | head -20

A flat first list and a second list dominated by one or two dynamic paths means a Layer 7 flood.

How do you stop an HTTP flood in your own stack?

All of this works on any server and any protection tier. Set it up before you need it, because writing limits for the first time during an attack is slow and error prone.

Cache everything that can be cached

A page served from cache touches no application code and no database. Full page caching for anonymous visitors turns most of a flood against public pages into cache hits. Strip unknown query parameters from the cache key so random strings cannot force a miss. For WordPress sites, see our web hosting and WordPress use case.

Rate limiting with nginx limit_req and limit_conn

limit_req caps requests per second per client address and limit_conn caps open connections. A starting point:

# http context
limit_req_zone  $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone  $binary_remote_addr zone=search:10m  rate=1r/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;

server {
    client_header_timeout 10s;
    client_body_timeout   10s;
    keepalive_timeout     15s;
    limit_conn perip 20;

    location /static/ { root /srv/app; }

    location / {
        limit_req zone=general burst=40 nodelay;
        proxy_pass http://app_servers;
    }
    location /search {
        limit_req zone=search burst=5 nodelay;
        proxy_pass http://app_servers;
    }
    location = /login {
        limit_req zone=login burst=5;
        proxy_pass http://app_servers;
    }
}

Static files sit outside the limit because every page view pulls a burst of assets. Offices and mobile carriers put many people behind one address, so keep per IP limits generous. Behind a load balancer or proxy service, $binary_remote_addr is the proxy's address, so set the real client IP with set_real_ip_from and real_ip_header.

Tighter limits on expensive routes

Search, login, password reset, checkout and report exports deserve their own zones at much lower rates, as the /search and /login blocks show. A real person does not search ten times a second. Inside the application, cap page sizes and set a database statement timeout. If the database saturates first, see our guide to sizing database servers.

Timeouts against slow request attacks

A slow request attack holds connections open by sending headers a few bytes at a time. Short header and body timeouts plus limit_conn take most of the sting out, and an event driven proxy such as nginx or HAProxy passes only complete requests to your workers.

Move heavy work off the request path

If a request triggers a PDF, an email or a report, put it on a queue and return straight away. A flood then fills a queue you can drain or discard instead of pinning every web worker. See splitting background jobs off your web server.

Put a reverse proxy in front of the app servers

A reverse proxy gives you one place to cache, rate limit and set timeouts before traffic reaches the application, and keeps the app servers' addresses out of public view. Let them accept HTTP only from the proxy. Our nginx reverse proxy guide covers the setup.

When does rate limiting stop working against a DDoS?

Per IP limits work when a few hundred addresses each send far more than any person would. They fail when the attacker spreads the load so every address stays under your limit.

As an example, your search limit is 1 request per second per IP. An attacker with 20,000 addresses sends one search per address every two seconds. That is 10,000 searches per second, every IP under the limit, and 400 cores of work at 40 ms each. Each request looks like someone typing a query.

Tightening your own stack from here hurts:

  • Lower the per IP limit and you block real customers first, especially behind shared addresses.
  • Add a global limit and the attack takes the whole allowance, so real searches are refused with it.
  • Add servers and you pay for capacity the attacker can match for less.

Separating these requests from real visitors needs a wider view of the traffic than one server's access log, and capacity in front of the server to filter while the flood runs. That is the job of upstream L7 DDoS mitigation.

When is upstream Layer 7 DDoS protection worth paying for?

At voxa.host, Layer 7 filtering exists on one tier only: 10 Tbps + Layer 7. Every other tier filters at Layer 3 and Layer 4, and HTTP floods on those servers are handled in your own stack.

TierFiltered upstreamHTTP floods handled byPer server, per month
100 GbpsL3/L4Your own stackIncluded
500 Gbps to 5 TbpsL3/L4Your own stack€156.45 to €2,623.95
10 Tbps + Layer 7L3/L4/L7Upstream filtering, plus your own stack€4,723.95

Prices are monthly, cover every IP address on the server and exclude VAT. There is no per attack fee. Add the tier at checkout, or change it later from the client area.

Revenue at risk per hour

As an example, a business taking €30,000 a day through its web application loses about €1,250 for each hour it cannot sell, more at peak. At that rate the tier costs about four hours of downtime a month. If one flood at the wrong moment would cost more, the arithmetic favours paying. If an hour offline costs €50, it does not.

Repeat targeting

One flood is an incident. Floods that return every time you tighten your limits mean the attacker is adapting, and each round costs your team another evening.

No time to tune limits during an incident

Self defence needs someone who knows the application to read logs and change limits while the flood runs. If that is one of two developers with a full roadmap, or nobody is on call, the real cost is hours, not hardware. Upstream filtering takes the first line of that work off your team.

When it is not worth it

A brochure site, a documentation portal or a shop whose pages cache well can usually ride out an HTTP flood with the steps above and the included L3/L4 protection. Our post on how much DDoS protection you need covers sizing the L3/L4 tiers.

Because the tier is priced per server, put it on the server that faces the internet: one reverse proxy carrying 10 Tbps + Layer 7, with app servers that accept traffic only from it.

Can a CDN or proxy service in front of the origin stop HTTP floods?

The other route is a third party CDN or reverse proxy service. DNS points at the service, it terminates TLS, caches what it can, filters, and forwards the rest to your server, the origin. It works with any voxa.host server on any tier. The trade off is that the service sees your traffic decrypted, so check your data protection obligations.

It protects only traffic that goes through it, so the origin address must stay unknown:

  • Use an address that has never been in public DNS. DNS history services keep old records, so an address that was public before the move is easy to find.
  • Check every hostname. A mail, staging or API subdomain, or an MX record, pointing at the same server gives the address away.
  • Watch what the server sends out. Email headers, link previews, pingbacks and webhook tests can all reveal it.
  • Firewall the origin. Accept ports 80 and 443 only from the service's published ranges, as in our firewall guide. What remains is L3/L4 traffic, which the server's own L3/L4 protection covers.

Next step: decide where your HTTP floods get handled

For most business web apps, HTTP flood protection starts in your own stack: cache, rate limit the expensive routes, set timeouts and put a reverse proxy in front, on top of the 100 Gbps of L3/L4 protection every VPS and dedicated server includes. When floods spread across too many addresses to separate and an hour offline costs more than the tier, 10 Tbps + Layer 7 adds upstream HTTP filtering. Compare every tier on our DDoS protection page.

Billed monthly · no setup fee

Deploy your first server now.

No minimum term. Intel Xeon builds carry a delivery estimate of under an hour; AMD EPYC builds are made to order. Every configuration has full root, ECC memory and a guaranteed 1 Gbps unmetered port.

$ curl https://my.voxa.host/api/v1/products -H "Authorization: Bearer fbk_…"

Dedicated billed monthly · No setup fee · Full root on every server

Included with every dedicated server
IPv4 address
One included
Unmetered port
1 Gbps guaranteed
DDoS protection
100 Gbps · L3/L4
Root access
Included
Setup fee
€0.00
Minimum term
None