CVE-2026-84380 Overview
CVE-2026-84380 affects HTTPX2, a next-generation HTTP client for Python maintained by Pydantic. The vulnerability exists in Request._prepare() within src/httpx2/httpx2/_models.py prior to version 2.11.0. The client adds a body-derived Content-Length header even when the caller already supplied a Transfer-Encoding header. This produces HTTP/1.1 requests carrying both framing headers, enabling request smuggling or connection desynchronization when downstream intermediaries disagree on precedence. The flaw is classified under CWE-444: Inconsistent Interpretation of HTTP Requests.
Critical Impact
Attackers positioned behind a shared proxy can smuggle requests, poison connections, or bypass front-end security controls when applications use HTTPX2 with caller-supplied Transfer-Encoding headers.
Affected Products
- Pydantic HTTPX2 versions prior to 2.11.0
- Python applications using HTTPX2 for outbound HTTP/1.1 requests
- Deployments where HTTPX2 traffic transits intermediaries such as proxies, load balancers, or WAFs
Discovery Timeline
- 2026-09-02 - CVE-2026-84380 published to NVD
- 2026-09-02 - Last updated in NVD database
Technical Details for CVE-2026-84380
Vulnerability Analysis
HTTPX2 constructs outbound HTTP/1.1 requests by merging caller-supplied headers with a set of default framing headers. The Request._prepare() method iterates over defaults and calls setdefault() for each entry independently. This treats Content-Length and Transfer-Encoding as unrelated headers instead of mutually exclusive framing directives defined by RFC 7230.
When a caller explicitly sets Transfer-Encoding: chunked, HTTPX2 still appends a body-derived Content-Length computed from fixed-size byte, JSON, form, or known-length multipart bodies. The resulting request contains both headers on the wire. Front-end and back-end HTTP servers frequently disagree on which header takes precedence, and that disagreement is the primitive that enables HTTP request smuggling.
Root Cause
The defect is a header-processing logic error. The pre-patch code only skipped the default Transfer-Encoding when the request already carried an explicit Content-Length. It did not perform the symmetric check: skip the default Content-Length when the request already carried an explicit Transfer-Encoding. Because the two headers are mutually exclusive for HTTP/1.1 message framing, treating them independently allowed both to co-exist on the same request.
Attack Vector
Exploitation requires an application that forwards or accepts attacker-influenced Transfer-Encoding values into HTTPX2 requests, or that itself sends chunked requests through a shared intermediary. Once the malformed request reaches a proxy chain, differential parsing between the front-end and back-end lets an attacker prepend or append bytes to another user's request. Consequences include cache poisoning, credential capture via response queue desync, and bypass of front-end access controls.
def _prepare(self, default_headers: dict[str, str]) -> None:
for key, value in default_headers.items():
- # Ignore Transfer-Encoding if the Content-Length has been set explicitly.
+ # Ignore Content-Length if Transfer-Encoding has been set explicitly.
+ if key.lower() == "content-length" and "Transfer-Encoding" in self.headers:
+ continue
+ # Ignore Transfer-Encoding if Content-Length has been set explicitly.
if key.lower() == "transfer-encoding" and "Content-Length" in self.headers:
continue
self.headers.setdefault(key, value)
Source: GitHub Commit 829b93a — patch that adds the symmetric check ensuring the two framing headers are treated as mutually exclusive.
Detection Methods for CVE-2026-84380
Indicators of Compromise
- Outbound HTTP/1.1 requests originating from Python services that contain both Content-Length and Transfer-Encoding: chunked headers.
- Proxy or WAF logs recording 400-class framing errors, unexpected connection resets, or requests flagged as malformed when originating from services using HTTPX2.
- Unexplained cross-user response mixing, cache poisoning entries, or access-control bypass events on services fronted by a shared proxy.
Detection Strategies
- Software composition analysis for httpx2 < 2.11.0 across Python build manifests, container images, and lockfiles.
- Inspect outbound traffic at the egress proxy for requests containing both framing headers; flag any HTTP/1.1 message that presents both Content-Length and Transfer-Encoding.
- Fuzz internal HTTP clients with chunked bodies and confirm downstream intermediaries reject dual-framing requests rather than forwarding them.
Monitoring Recommendations
- Alert on WAF or reverse-proxy rules that flag HTTP request smuggling signatures on paths served by HTTPX2 clients.
- Track version drift for the HTTPX2 dependency in production build pipelines and fail builds that resolve versions below 2.11.0.
- Correlate anomalous response-body sizes and unexpected Connection: close events with source services identified as HTTPX2 consumers.
How to Mitigate CVE-2026-84380
Immediate Actions Required
- Upgrade HTTPX2 to version 2.11.0 or later in every affected service and rebuild dependent container images.
- Audit application code for call sites that set Transfer-Encoding explicitly and validate the resulting request headers on a test proxy.
- Enforce strict HTTP parsing on intermediaries so that requests containing both Content-Length and Transfer-Encoding are rejected rather than forwarded.
Patch Information
The fix is included in HTTPX2 version 2.11.0. The patch in Request._prepare() adds a symmetric check that skips the default Content-Length when the request already contains a Transfer-Encoding header, restoring mutual exclusivity between the two framing headers. See GitHub Pull Request #1137, the GitHub Release v2.11.0, and the GitHub Security Advisory GHSA-pf96-p4fj-6566.
Workarounds
- Remove caller-supplied Transfer-Encoding headers before invoking HTTPX2 request methods and let the client compute framing from the body.
- Route HTTPX2 traffic exclusively over HTTP/2 endpoints where message framing is length-prefixed and immune to CL.TE style desync.
- Deploy a reverse proxy in front of downstream services configured to reject any HTTP/1.1 request containing both framing headers.
# Pin HTTPX2 to the fixed version in requirements.txt
httpx2>=2.11.0
# Verify the resolved version in a running environment
python -c "import httpx2; print(httpx2.__version__)"
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.