the short version
- Any process that decodes attacker-controlled Brotli with Google's decoder 1.0.3 through 1.2.0 is exposed. The attacker can be either a client or a server.
- Python services are exposed without importing Brotli. urllib3, requests, httpx and aiohttp decode it automatically when Brotli or brotlicffi is installed.
- As of October 23, pip-audit, Dependabot and Safety do not flag vulnerable Brotli or brotlicffi versions, so teams have to check by hand.
- Removing br from Accept-Encoding does not protect a client, because a malicious server can send a Brotli response anyway.
HTTP2Shell is CVE-2026-111497, an out-of-bounds write in Google's Brotli decoder in versions 1.0.3 through 1.2.0, rated CVSS v4.0 9.2 (Critical). It affects Node.js, Bun, Envoy, Istio and curl. It also affects the Python packages Brotli and brotlicffi, which requests, httpx and aiohttp load automatically when present and which Scrapy installs as a dependency. To check a service, look for Brotli 1.2.0 or earlier or brotlicffi 1.2.0.x in every Python environment, and for unpatched Node.js, Bun, Envoy or Istio anywhere in the request path. The fixes are Brotli 1.2.1, brotlicffi 1.2.1.0, Node.js 22.23.4, 24.22.1 or 26.12.1, Bun 1.4.4, and matching Envoy and Istio releases.
Node.js disclosed the flaw first, on Wednesday, under the name Node2Shell. Aikido's post gives that date as both October 21 and October 22. On Thursday, Python said its packages were vulnerable too. By Friday, October 23, Aikido reported that other languages and HTTP applications were affected and that patches were not yet fully available. Aikido says a remote, unauthenticated attacker can corrupt memory, leak data, crash the process or potentially execute code, and that a malicious client can take over an unpatched server while a malicious server can take over an unpatched client that accepts compressed responses.
Why one Brotli bug reaches many runtimes
The bug is in DecodeBlockTypeAndLength(), the function that handles block switches, the points where a Brotli stream changes which Huffman tree it uses. In vulnerable versions it returns a plain boolean, and BROTLI_FALSE can mean either a malformed stream or a need for more input. Callers cannot tell the two apart, and on the fast path the decoder discards the return value entirely. A crafted stream can declare one block type and then exhaust its length, which forces a switch that cannot succeed. The decoder then either reports that it needs more input for a complete stream or keeps decoding with the block state it already had, and that second path leads to the out-of-bounds write.
The upstream fix, google/brotli commit 0ed4268, makes the function return an error code and adds BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH (-17). Decoding now stops on a format error on both the safe and fast paths. One commenter on Hacker News asked why Python, Node, C++, libbrotli, Envoy and Bun are all vulnerable at once. Aikido says the two PyPI packages compile the same vulnerable decoder that Node.js bundles, and that every project bundling Brotli has to ship its own update. Aikido also says Apache's standard mod_brotli does not decode Brotli, and that Apache and Nginx do not decode Brotli by default.
Several commenters on Hacker News objected to the name. One argued it probably does not affect an HTTP server with nothing else behind it. Another said they first assumed HTTP/2 was the problem. The sources support the narrower reading: exposure depends on whether something in the stack actually decodes Brotli, not on whether it speaks HTTP.
Which Python HTTP clients load the decoder
A Python environment is affected if it has Brotli 1.2.0 or earlier, or brotlicffi 1.2.0.x, installed. Brotli has 82.8 million downloads per month. Your code does not need to import either package. The following clients advertise Accept-Encoding: br and decode Brotli responses automatically:
- urllib3, and therefore requests, when Brotli or brotlicffi is installed
- httpx with the brotli extra
- aiohttp when Brotli is installed
- Scrapy, which installs Brotli as a dependency, and anything built on the clients above
Attacker data reaches the decoder by one of two routes. On the outbound route, your service fetches a Brotli response from a server the attacker controls. Aikido's examples are webhook delivery, URL previews, crawlers and fetch-this-URL features. On the inbound route, framework or middleware code decodes a request body sent with Content-Encoding: br, which Aikido calls the Python equivalent of the Express 5 case in Node.js. For Node, Aikido gives the built-in fetch() and body-parser as examples of code that decompresses Brotli data.
How to check an API or AI service
The sources do not name any model-serving framework, so there is no vendor list for AI stacks yet. The practical test is whether anything in the request path decodes Brotli. Candidates include Envoy or Istio in front of the service, Python or Node backends that accept compressed bodies, and any feature that fetches user-supplied URLs. Retrieval and agent features that fetch arbitrary pages sit on the same outbound route Aikido describes for crawlers. Aikido says the release notes for the fixed Python packages do not mention the CVE, and as of October 23 advisory databases do not map it to either PyPI package, so pip-audit, Dependabot and Safety will not flag vulnerable versions.
# in every environment: laptops, CI runners, servers, containers
pip show Brotli brotlicffi
# upgrade whichever is present
pip install -U "Brotli>=1.2.1"
pip install -U "brotlicffi>=1.2.1.0"Check the full resolved dependency tree, because many projects pull in Brotli through an HTTP client extra. If you use Poetry, uv or pip-tools, update the constraint and regenerate the lockfile. Then rebuild container images and any wheels that bundle either package, because an image built on a vulnerable version stays vulnerable until it is rebuilt. If your Python Brotli bindings came from a Linux distribution instead of PyPI, the fix comes through that distribution, so check its security advisory. For managed services, confirm remediation with the provider.
Fixed versions that close HTTP2Shell
- Node.js: 22.23.4, 24.22.1 or 26.12.1, or a newer supported release in the same line
- libbrotli / Google Brotli: 1.2.1, or patched 1.0.10 or 1.1.1 for older release lines
- Python: Brotli 1.2.1 or later and brotlicffi 1.2.1.0 or later, wherever installed
- Bun: 1.4.4 or later, which includes Brotli 1.2.1; rebuild compiled executables that bundle Bun
- Envoy: 1.40.0, 1.39.3, 1.38.6 or 1.37.8, or newer in a supported branch; 1.36.12 has the fix, but the 1.36 branch is unsupported
- Istio: 1.29.9, 1.30.6 or 1.31.2, or newer in a supported line; the 1.29 branch is end of life
- curl / libcurl: move its Brotli dependency to 1.2.1, a patched older release or a vendor-fixed package; bundled or statically linked copies need an updated application build
- OS packages: install the vendor's security update, which may backport the fix without changing the upstream version number
OSM diffed the Brotli 1.2.0 and 1.2.1 source distributions and found that 1.2.1 differs only in the version bump and the decoder changes from commit 0ed4268. For other HTTP servers, reverse proxies, API gateways and clients that bundle Brotli, Aikido says to install the vendor release that explicitly addresses this CVE. After upgrading, update dependency locks, rebuild, redeploy, restart services, and verify that running workloads use the patched components.
Mitigations when you cannot upgrade yet
- Python: where Brotli support is optional, uninstall both Brotli and brotlicffi, remove extras such as httpx[brotli], regenerate locks and rebuild or restart workloads. HTTPX, Requests and urllib3 keep gzip and deflate support, but a server that sends Brotli anyway may produce an unreadable response or an application error, and automatic fallback is not guaranteed.
- Inbound: reject requests whose Content-Encoding includes br at the reverse proxy, load balancer or application middleware, before any vulnerable decoder processes the body. Match br case-insensitively, including within lists of encodings.
- Outbound: remove br from Accept-Encoding, and also disable Brotli response decoding or reject br responses before automatic body processing begins. Checking after the client has downloaded and decoded the response is too late.
Changing Accept-Encoding alone is insufficient: a malicious server can send a Brotli response regardless of what the client advertises
Aikido adds that urllib3 selects its decoder from the response's Content-Encoding header, which is why the client-side header change is not enough. Uninstalling the Python packages removes only the decoding paths those packages provide. It does not remove separate Brotli decoders bundled into other runtimes, libraries or products, which need their own upgrades. Aikido recommends keeping these controls until affected components are patched and verified.
The flaw was fixed in February, then forgotten
According to an update in Aikido's post, OpenAI says it discovered the vulnerability in February and reported it as unauthenticated remote code execution that works in both directions. The root cause was fixed in the Brotli source code in February. Nobody recognized it as a security issue at the time, and no release shipped with the fix until now. One commenter on Hacker News doubted the OpenAI account and suggested Google changed the code in February without realizing it was a security fix. Another quoted the commit message as "Detect and report invalid stream earlier." The sources do not explain the gap between the report and the release.
One commenter on Hacker News noted that dependency cooldowns, widely advised a few months ago, now keep getting overridden for security incidents. One reply argued for deploying security fixes that apply to you immediately and putting feature updates behind a cooldown. That reply also said a lockfile with few dependencies makes it easier to tell which upgrades are urgent. Another reply said teams should practise both shipping quickly and overriding cooldowns, and that cooldowns are still the better risk-reduction strategy most of the time.
Questions this raises
Is my Python service affected by HTTP2Shell if my code never imports Brotli?
It can be. urllib3 (and therefore requests), httpx with the brotli extra, and aiohttp decode Brotli responses automatically when Brotli or brotlicffi is installed, and Scrapy installs Brotli as a dependency. An environment with Brotli 1.2.0 or earlier or brotlicffi 1.2.0.x is affected.
Why won't pip-audit or Dependabot flag the vulnerable Brotli versions?
According to Aikido, the release notes for the fixed Python packages do not mention the CVE. As of October 23, advisory databases do not map it to either PyPI package, so pip-audit, Dependabot and Safety will not flag vulnerable versions. Check the full resolved dependency tree manually.
Does HTTP2Shell affect every HTTP or HTTP/2 server?
No. Exposure depends on whether something in the stack actually decodes Brotli, not on whether it speaks HTTP. Aikido says Apache's standard mod_brotli does not decode Brotli, and Apache and Nginx do not decode Brotli by default.
Spotted an error? Email samir.s.sengupta@gmail.com and I will correct it and date the change at the top of the note. How these notes are researched, drafted and checked is in the editorial policy.
