HTTP/2 single-connection DoS attack

Hello, haproxy users!

yesterday one of my webservers using haproxy as a frontend was subject to an unusual (D)DoS attack: According to the logs, the attack was from not so many remote clients (maybe several hundreds or small thousands), all sending requests to the same URL, but each one sending enormous number of requests inside a single HTTP/2 connection. According to my haproxy log, one such client managed to send over one million requests in 6 seconds, with peak of 1.2M requests in one second (at least that many requests finished in one second and were logged by haproxy). The incoming traffic was about 2-3 Gbit/s at that time. This exhausted CPUs on my haproxy server, so haproxy did not manage to complete TLS handshakes with regular clients, and the attack also exhausted all backend servers up to their maxconn, and my web site became unavailable.

In the log file, filtered by a single IP address, all requests were from the same source port, so I presume they went through the same TCP connection. I briefly ran tcpdump on my router, and indeed it looked like big traffic over not so many connections.

I blacklisted the single URL which all the attackers used, so in the log I can see requests ending with 403 like this:


Dec 15 20:11:57 localhost haproxy[1126404]: that.ip.add.ress:46692 [15/Dec/2025:20:11:57.755] frontend~ frontend/<NOSRV> 0/-1/-1/-1/0 403 9678 - - PR-- 8284/8284/0/0/0 0/0 "GET https://my.host.name/prefix/ HTTP/2.0"
Dec 15 20:11:57 localhost haproxy[1126404]: that.ip.add.ress:46692 [15/Dec/2025:20:11:57.755] frontend~ frontend/<NOSRV> 0/-1/-1/-1/0 403 9678 - - PR-- 8284/8284/0/0/0 0/0 "GET https://my.host.name/prefix/ HTTP/2.0"
Dec 15 20:11:57 localhost haproxy[1126404]: that.ip.add.ress:46692 [15/Dec/2025:20:11:57.755] frontend~ frontend/<NOSRV> 0/-1/-1/-1/0 403 9678 - - PR-- 8284/8284/0/0/0 0/0 "GET https://my.host.name/prefix/ HTTP/2.0"
Dec 15 20:11:57 localhost haproxy[1126404]: that.ip.add.ress:46692 [15/Dec/2025:20:11:57.755] frontend~ frontend/<NOSRV> 0/-1/-1/-1/0 403 9678 - - PR-- 8284/8284/0/0/0 0/0 "GET https://my.host.name/prefix/ HTTP/2.0"


but many more <BADREQ> requests like this:

Dec 15 20:11:57 localhost haproxy[1126404]: that.ip.add.ress:46692 [15/Dec/2025:20:11:57.785] frontend~ frontend/<NOSRV> -1/-1/-1/-1/0 0 0 - - PR-- 8286/8286/0/0/0 0/0 "<BADREQ>"
Dec 15 20:11:57 localhost haproxy[1126404]: that.ip.add.ress:46692 [15/Dec/2025:20:11:57.785] frontend~ frontend/<NOSRV> -1/-1/-1/-1/0 0 0 - - PR-- 8286/8286/0/0/0 0/0 "<BADREQ>"
Dec 15 20:11:57 localhost haproxy[1126404]: that.ip.add.ress:46692 [15/Dec/2025:20:11:57.785] frontend~ frontend/<NOSRV> -1/-1/-1/-1/0 0 0 - - PR-- 8286/8286/0/0/0 0/0 "<BADREQ>"
Dec 15 20:11:57 localhost haproxy[1126404]: that.ip.add.ress:46692 [15/Dec/2025:20:11:57.785] frontend~ frontend/<NOSRV> -1/-1/-1/-1/0 0 0 - - PR-- 8286/8286/0/0/0 0/0 "<BADREQ>"
Dec 15 20:11:57 localhost haproxy[1126404]: that.ip.add.ress:46692 [15/Dec/2025:20:11:57.785] frontend~ frontend/<NOSRV> -1/-1/-1/-1/0 0 0 - - PR-- 8286/8286/0/0/0 0/0 "<BADREQ>"
Dec 15 20:11:57 localhost haproxy[1126404]: that.ip.add.ress:46692 [15/Dec/2025:20:11:57.785] frontend~ frontend/<NOSRV> -1/-1/-1/-1/0 0 0 - - PR-- 8286/8286/0/0/0 0/0 "<BADREQ>"
Dec 15 20:11:57 localhost haproxy[1126404]: that.ip.add.ress:46692 [15/Dec/2025:20:11:57.785] frontend~ frontend/<NOSRV> -1/-1/-1/-1/0 0 0 - - PR-- 8286/8286/0/0/0 0/0 "<BADREQ>"
Dec 15 20:11:57 localhost haproxy[1126404]: that.ip.add.ress:46692 [15/Dec/2025:20:11:57.785] frontend~ frontend/<NOSRV> -1/-1/-1/-1/0 0 0 - - PR-- 8286/8286/0/0/0 0/0 "<BADREQ>"

For that particular client/attacker that.ip.add.ress:46692, I have 1648 GET https://my.host.name/prefix/ and 1342086 <BADREQ> requests from 20:11:57 to 20:12:03 in my log file.

Is it possible to limit how many in-flight requests a single HTTP/2 connection can have? Or how many total requests can be issued per connection? I presume H3/QUIC would have the same problem.

I mitigated this particular (D)DoS attack by disabling HTTP/2 in ALPN and forcing the HTTP/1.1 protocol in the frontend config section:

        bind my.ip.add.ress:443 proto h1 ssl crt-list mysite.list alpn http/1.1

Thanks for any configuration recommendations.

-Yenya

Take a look at:

tune.h2.max-concurrent-streams

as well as:

tune.h2.fe.max-total-streams

Lukas

1 Like

Hi Lukas,

thanks for the hint. If I understand the docs correctly, tune.h2.max-concurrent-streams is set to 100 by default. I can set it to 4 or whatever low value, but I think my server was out of available CPU even when this limit has been reached. So I think rejecting the streams which go over this limit is not so cheap either.

I will try this later.

Thanks!