UnveilTech

UnveilScan Blog

← All articles

Try UnveilScan free

HTTP request smuggling: what passive scanners miss

Posted 2026-04-29 · 8 min read · WEBadvanced

HTTP request smuggling exploits parser disagreement between two HTTP servers in series — typically a CDN/reverse-proxy in front of an origin. The 2019 Burp Suite paper made it widely known; the technique has been around since 2005. It remains, in 2026, an extremely high-impact bug class that passive scanners (including ours) cannot reliably detect.

The core ambiguity

HTTP/1.1 specifies two ways to delimit the body of a POST: Content-Length and Transfer-Encoding: chunked. RFC 7230 says: if both are present, the recipient must process Transfer-Encoding and ignore Content-Length.

In practice, different servers implement this rule differently:

StackIf both headers present
nginx (older)Used CL, ignored TE
HAProxyUsed TE
AWS ALBHistorically used TE; CL after parsing edge cases
IISUsed TE
Apache 2.4Used TE; obscure cases used CL
TomcatUsed CL in some versions
Node.js (older)Used CL

An attacker crafts a request the front-end parses as one shape (one request) and the back-end parses as two requests. The "smuggled" second request is concatenated to the next legitimate request's start.

The CL.TE attack

POST / HTTP/1.1
Host: vulnerable.example.com
Content-Length: 13
Transfer-Encoding: chunked

0

SMUGGLED

Front-end uses Content-Length=13 (sees the whole thing as one request). Back-end uses Transfer-Encoding (sees the 0\r\n\r\n as end-of-body, leaves SMUGGLED in the connection buffer). The next legitimate request from anyone gets SMUGGLED prepended to its start.

What you can do with that

In 2024 a major US healthcare provider had a CL.TE bug exploited that exposed PHI for ~2 million users. Bug bounty payouts of $5K-$30K for HRS findings on PortSwigger Labs. This is not theoretical.

HTTP/2 doesn't save you

HTTP/2 forbids both Content-Length and Transfer-Encoding in the wire format — frames have explicit lengths. But: most edges accept HTTP/2 from clients and downgrade to HTTP/1.1 to talk to the origin. The downgrade re-introduces the ambiguity and adds new ones (e.g., :path pseudo-header to Host+URI conversion, header-name newline injection).

James Kettle's 2021 "HTTP/2: The Sequel" paper documented H2.CL, H2.TE, and H2.X classes of smuggling on H2-to-H1 downgrades. Vulnerable stacks at the time: AWS ALB, Atlassian, Cisco WebEx.

Why we don't probe for it

Our scanner is passive by design (Basic + Extended profiles). Probing for smuggling requires:

This is the line between passive and active we don't cross. Active profile checkers in UnveilScan have a triple-gate: ownership, ack, non-destructive. A smuggling probe that risks corrupting a CDN cache for other users on the same edge isn't non-destructive in any meaningful sense.

What you should run instead

Mitigation patterns

  1. Front-end normalizes. Configure the edge proxy to reject requests with both CL and TE. nginx proxy_http_version 1.1; proxy_set_header Connection "close"; partly helps but doesn't catch all variants.
  2. Back-end strict mode. Apache HttpProtocolOptions Strict, nginx with http2_body_preread_size 0, Node.js http.maxHeaderSize.
  3. End-to-end HTTP/2 or HTTP/3. No 1.1 downgrade = no ambiguity-based smuggling. Deploy this where you can.
  4. Connection-per-request. Disable keepalive between front-end and back-end. Smuggling requires connection reuse to inherit between users. Performance hit, sometimes acceptable for sensitive apps.

What we DO check

We check upstream stack hygiene that often correlates with smuggling exposure: the protocols checker reports HTTP/2 + HTTP/3 support; the headers checker flags Server banners that reveal old versions (nginx 1.13, HAProxy 1.7) where smuggling bugs were unfixed; the techcve checker cross-references those banners against OSV.dev for known CVEs.

These are weak signals. They tell you "look here" — not "you have HRS." For the actual HRS audit, pay a pentester or run smuggler.py in CI.

Audit your edge stack hygiene

Free Basic scan reports your front-end stack version and known CVEs. Pair it with a real HRS test.

Run a scan