HTTP/2 Bomb: how one laptop takes down nginx, Apache, and IIS in seconds
HTTP/2 Bomb: how one laptop takes down nginx, Apache, and IIS in seconds
On June 3, 2026, Calif published HTTP/2 Bomb — a remote DoS exploit against nginx, Apache httpd, Microsoft IIS, Envoy, and Cloudflare Pingora. No botnet required: a standard home machine on a 100 Mbps connection brings a vulnerable server to its knees in seconds. Against Apache httpd and Envoy, a single client can consume and hold 32 GB of server memory in roughly 20 seconds.
The attack wasn’t found by a human — it was found by OpenAI Codex. Researcher Quang Luong tasked the model with analyzing the codebases of popular HTTP/2 servers for dangerous combinations of known techniques. Codex chained two primitives, each publicly known for over a decade, and produced an attack that none of the affected server developers had anticipated.
For nginx the vulnerability is fixed in 1.29.8 (mainline) and 1.30.0+ (stable). If you already updated to 1.30.2 following the previous CVEs — nginx-rift and CVE-2026-9256 — your server is covered. Apache httpd has a patch in mod_http2 v2.0.41, tracked as CVE-2026-49975. IIS, Envoy, and Cloudflare Pingora have no patches at time of writing.
WHAT IS HTTP/2 BOMB
Picture a librarian with a perfect memory: you name a book once, they shelve it, and from that point you can request it with a single word — they’ll always fetch the full copy. HTTP/2 HPACK works the same way: the sender adds a header to a dynamic table once, then references it with a single byte. The receiver materializes the full header structure in memory on every reference. Great for saving bandwidth under normal conditions. The problem is when the header is tiny and there are thousands of references.
HTTP/2 (RFC 9113) adds flow control: the receiver advertises a window size, and the sender can’t push data beyond that window until it gets a WINDOW_UPDATE. The catch is that the client controls the window for the server’s responses. That means a client can keep a stream open indefinitely, dripping in minimal WINDOW_UPDATE frames to prevent timeout closure. HTTP/2 Bomb chains both mechanisms: pump memory fast through HPACK, then prevent it from ever being freed through Window Stall.
HOW THE BUG WORKS
Part one is the HPACK Indexed Reference Bomb. The attacker seeds the dynamic table with a single header, then fires thousands of one-byte indexed references to it within a single request. Each reference costs the attacker one byte on the wire but forces the server to allocate a full header structure. The amplification ratio varies by implementation:
| Server | Amplification | Demo result |
|-------------------------------------|---------------|------------------|
| Envoy 1.37.2 | ~5,700:1 | ~32 GB in ~10s |
| Apache httpd 2.4.67 | ~4,000:1 | ~32 GB in ~18s |
| nginx 1.29.7 | ~70:1 | ~32 GB in ~45s |
| Microsoft IIS (Windows Server 2025) | ~68:1 | ~64 GB in ~45s |
The reason existing defenses didn’t catch this is worth understanding. Previous HPACK bombs (CVE-2016-6581) stuffed a large value into the table and referenced it repeatedly, so servers learned to cap the total decoded header size. HTTP/2 Bomb goes the other direction: the header value is nearly empty, and the amplification comes from the per-entry allocator overhead around each record. The decoded-size limit never fires because there’s almost nothing to decode.
Part two is HTTP/2 Window Stall. The attacker advertises a zero-byte flow-control window for the server’s response. The server can’t finish sending its response, can’t close the stream, can’t free memory. To prevent the connection from timing out, the attacker periodically drips in minimal WINDOW_UPDATE frames — the server sees activity and keeps the stream alive. All allocated memory stays pinned for as long as the attacker holds the connection.
Together these two techniques create a situation where memory is allocated fast and never freed. As Calif notes, OOM-killing the process is actually counterproductive — a killed worker just restarts clean. The more effective play is to hold memory pressure just below the OOM-killer threshold, push the machine into swap, and wait for every request on the server to start crawling.
HOW IT IS EXPLOITED
Apache and Envoy both cap the number of header fields — that sounds like protection. But RFC 9113 §8.2.3 explicitly allows splitting the Cookie header into separate fields, one per crumb. Neither Apache nor Envoy counted those crumbs against their header field limit. The bypass is elegant: walk through the entrance the spec itself left open.
In Envoy, each cookie crumb gets appended to a buffer — a 4 KB value referenced 32,000 times yields ~3,600:1 logical amplification, with measured RSS ratios reaching ~5,700:1 on a single stream once allocator overhead stacks up. Apache httpd rebuilds the entire merged cookie string on every new crumb, leaving each older copy live until stream cleanup — so even an empty cookie value gives ~4,000:1.
PoC scripts, Docker labs, and per-server writeups are published on GitHub at califio/publications/tree/main/MADBugs/http2-bomb. The commit-to-exploit path is short — Calif explicitly warn that any capable AI model can turn the public patch diffs into a working exploit.
REAL-WORLD ATTACK CHAIN
The attacker opens an HTTP/2 connection to the server — no authentication, no prior knowledge of the configuration needed. They seed the HPACK dynamic table with a single header, then fire thousands of one-byte references to it in a single request while the server allocates structures and the attacker spends bytes. At the same time they advertise a zero window for the server’s response, preventing any stream from completing.
With nginx’s 70:1 amplification and a 100 Mbps upstream, an attacker can generate the equivalent of ~7 Gbps of memory pressure — without any botnet. Apache and Envoy at 4,000–5,700:1 hit 32 GB in 10–20 seconds on a typical VPS. The server doesn’t crash immediately — it degrades: new requests get slower and slower until they stop being processed at all.
A Shodan search found over 880,000 sites supporting HTTP/2 and running one of the affected servers. Many sit behind a CDN, which substantially reduces the risk — but direct installs with no proxying are fully exposed.
TIMELINE
In April 2026, Calif disclosed the vulnerability to the nginx team. The developers responded the next day, importing the max_headers directive from freenginx and shipping nginx 1.29.8. Commit: 365694160a85229a7cb006738de9260d49ff5fa2. The same max_headers directive with a default ceiling of 1,000 made it into the stable branch — nginx 1.30.0 shipped in April 2026 with the same protection.
On May 27, 2026, Calif disclosed to the Apache team. Stefan Eissing patched it the same day by making cookie headers count against the LimitRequestFields limit. Commit: 47d3100b252dc6668a9e46ae885242be9eeca9cd. The issue was assigned CVE-2026-49975.
On June 3, 2026, Calif published the full writeup, PoC scripts, and Docker labs. They also revealed that the public patch diffs were exactly what led them to discover the vulnerability in IIS, Envoy, and Pingora — the AI model read the commits and independently found the same patterns in other servers. Microsoft, Envoy, and Cloudflare have been notified; no patches at time of publication.
HOW DID THIS SURVIVE TEN YEARS
HPACK Bomb as a concept has been around since 2016 — CVE-2016-6581, Cory Benfield. Slowloris-style exhaustion via HTTP/2 also dates to 2016: CVE-2016-8740 and CVE-2016-1546. In 2025, Gal Bar Nahum hit ~4,000:1 amplification against Apache httpd via CVE-2025-53020. All of these techniques are public, documented, and were patched individually.
The problem is that each time a specific vector was closed, not the root cause. Servers learned to cap the total decoded header size — which is exactly why the new attack works through per-entry allocator overhead rather than value size. RFC 7541 has an entire §7.3 “Memory Consumption” section warning that an attacker might try to exhaust memory through HPACK. It explains that HPACK bounds the dynamic table via SETTINGS_HEADER_TABLE_SIZE and considers the matter handled. But when five independent implementations all read that section and still ship the same class of bug — the defect is in the spec, not the implementations.
The Calif writeup author adds a personal note: in 2012 he helped discover the CRIME attack against HTTP header compression, then was brought in to review the fix — which became HPACK. Re-reading his notes from that review, he found he’d never once considered this attack. He was too focused on fighting CRIME and missed the bomb.
WHY IT MATTERS
For nginx admins, this is the third critical vulnerability in a few months — after nginx-rift and CVE-2026-9256 (nginx-poolslip, CVSS 9.2). If you updated to 1.30.2 after those CVEs via the nginx.org repository, the max_headers directive is already in your build and active with a default of 1,000. If you’re still on Ubuntu’s 1.24.0 package, you’re exposed to all three.
This attack is fundamentally different from traditional DDoS. Traditional DDoS requires bandwidth: to saturate a 1 Gbps server link you need to generate 1 Gbps of traffic. HTTP/2 Bomb at 70:1 for nginx lets you create the equivalent of 7 Gbps of memory pressure from a 100 Mbps home connection. For Apache at 4,000:1 — the equivalent of 400 Gbps from that same link. CDNs like Cloudflare substantially reduce the risk through their infrastructure, but direct installs without proxying are fully exposed.
The way the attack was found is worth noting separately. Both primitives have been public for over a decade. What Codex did was read the server codebases, recognize the two techniques compose, and build the combined attack. It’s obvious in hindsight — and yet as far as anyone can tell, no human had put this combination together against these servers before.
UPDATE
Start by knowing where you stand. Check your nginx version and whether the protection is in place:
nginx -v
nginx -T | grep max_headers
If you’re on 1.30.0+ (stable) or 1.29.8+ (mainline), you’re already protected. If grep returns nothing, the directive isn’t set explicitly — but the default of 1,000 is active automatically from these versions. If your version is lower, read on.
Scenario 1: nginx.org repository already connected. Check what will be installed, then update:
apt-cache policy nginx
The Candidate: line should show 1.30.x from origin nginx.org. If it does:
apt update && apt install nginx
Scenario 2: still on Ubuntu’s 1.24.0 package. Set up the nginx.org repository. Fetch the GPG key through a pipe with dearmor, as the official nginx.org documentation specifies:
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
| sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
Verify the fingerprint — don’t skip this:
gpg --dry-run --quiet --no-keyring \
--import --import-options import-show \
/usr/share/keyrings/nginx-archive-keyring.gpg
Expected fingerprints (verify current ones at nginx.org/en/pgp_keys.html):
8540A6F18833A80E9C1653A42FD21310B49F6B46
573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62
9E9BE90EACBCDE69FE9B204CBCDCD8A38D88A2B3
If they don’t match, delete the file immediately. If they do, add the repository (stable 1.30.x) and pinning:
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
https://nginx.org/packages/ubuntu $(lsb_release -cs) nginx" \
| sudo tee /etc/apt/sources.list.d/nginx.list
cat > /etc/apt/preferences.d/nginx << 'EOF'
Package: nginx*
Pin: origin nginx.org
Pin-Priority: 900
EOF
apt update && apt install nginx
When apt prompts about the configuration file, choose N to keep your version. After updating, verify:
nginx -v && nginx -t && systemctl reload nginx
This update closes three vulnerabilities at once: nginx-rift, CVE-2026-9256, and HTTP/2 Bomb.
Scenario 3: dnf systems (RHEL, Rocky Linux, AlmaLinux). Same procedure as Ubuntu — add the nginx.org repository via nginx.org/en/linux_packages.html, using dnf instead of apt.
Apache httpd. The situation is trickier — the fix in standalone mod_http2 v2.0.41 hasn’t made it into the main httpd 2.4.x release, and there’s no apt package. The safest path right now is to disable HTTP/2:
# In httpd.conf or a virtual host config
Protocols http/1.1
apachectl configtest
systemctl reload apache2
curl -I --http2 https://example.com 2>&1 | grep -i "HTTP/"
If the response shows HTTP/1.1, HTTP/2 is off. If you want to install mod_http2 v2.0.41 and keep HTTP/2, the build instructions are in the README at github.com/icing/mod_h2. Once the patch lands in a main httpd release, switch to the standard package.
For all servers — an additional measure. Cap per-worker memory. A worker killed by the OOM killer and restarted clean beats a machine sliding into swap under an attacker’s control:
# /etc/systemd/system/nginx.service.d/override.conf
[Service]
LimitAS=2G
systemctl daemon-reload && systemctl restart nginx
IIS, Envoy, Cloudflare Pingora — no patches. Disable HTTP/2 or proxy through nginx 1.30.x.
CONCLUSIONS
HTTP/2 Bomb isn’t a new technique — it’s a new combination of old techniques that nobody assembled for ten years. One home computer, 100 Mbps, a few seconds — and your server degrades under a load equivalent to hundreds of gigabits.
If you’re on nginx 1.30.2, you’re already covered — nothing to do. If not, updating to 1.30.2 via the nginx.org repository closes three active vulnerabilities at once: nginx-rift, CVE-2026-9256, and HTTP/2 Bomb. One command, three problems.
