Cybersecurity Application Security

HTTP Request Smuggling: Detecting and Blocking CL-TE Desync Attacks

June 23, 2026 9 min read 1 views

Your reverse proxy and your back-end application read the same HTTP request β€” but they don't always agree on where it ends. That disagreement is the entire attack surface for HTTP request smuggling, and the CL-TE variant is the most widely exploited form in the wild.

If you run any architecture where a load balancer, CDN, or reverse proxy forwards traffic to a back-end server, you need to understand this class of bug. It can be used to bypass access controls, poison web caches, hijack other users' sessions, and perform reflected XSS against authenticated users.

What Is HTTP Request Smuggling?

HTTP request smuggling is an attack that manipulates the boundary between HTTP requests in a persistent connection. When the front-end proxy and the back-end server parse the same request using different rules, an attacker can "smuggle" a partial request into the pipeline so the back end treats it as the beginning of the next request.

The root cause is ambiguity in HTTP/1.1: the spec defines two headers that both describe message body length β€” Content-Length (CL) and Transfer-Encoding: chunked (TE). When both are present, different HTTP implementations make different choices about which one to trust.

  • Understand how CL-TE header conflicts create exploitable ambiguity.
  • Walk through a real CL-TE payload construction step by step.
  • Use timing-based and differential response techniques to detect the vulnerability.
  • Apply concrete server and proxy configuration fixes to eliminate the attack surface.
  • Avoid the common mistakes that leave systems exposed even after patching.

How CL-TE Desync Works

In a CL-TE attack, the front-end proxy prioritizes Content-Length and the back-end server prioritizes Transfer-Encoding: chunked. This asymmetry is the entire attack.

Here's the high-level flow:

  1. The attacker sends a request that contains both Content-Length and Transfer-Encoding: chunked.
  2. The front-end proxy reads the body using Content-Length and forwards the entire request to the back end over a persistent (keep-alive) connection.
  3. The back-end server reads the body using Transfer-Encoding: chunked. A chunked body ends when it sees a chunk of size 0. The attacker places the zero-size chunk terminator before the full body ends according to the Content-Length value.
  4. The back end stops reading the chunked body at the zero chunk, considers the request complete, and processes it. The leftover bytes β€” which the front end already handed over β€” now sit at the start of the back-end's read buffer, prepended to the next legitimate request that arrives.

That leftover prefix is the "smuggled" request. The back end thinks it's reading a new, legitimate request, but part of it was written by the attacker.

Why Front-End and Back-End Disagree

RFC 7230 states that if both Transfer-Encoding and Content-Length are present, Transfer-Encoding takes priority and Content-Length should be ignored. The problem is that not all implementations follow this rule uniformly, and some proxies strip or normalize headers before forwarding.

Common combinations that produce CL-TE behavior:

  • HAProxy (older versions) + Apache/Nginx: HAProxy may strip a malformed or obfuscated Transfer-Encoding header while Apache or Nginx still processes it.
  • AWS ALB + Gunicorn/uWSGI: Load balancers that normalize headers before passing to app servers can create similar disagreements.
  • Cloudflare + origin servers: CDN edge nodes apply their own HTTP parsing stack, which can differ from whatever your origin runs.

The attack is also viable when the attacker obfuscates the Transfer-Encoding header in ways the front end ignores but the back end processes β€” for example, adding a space before the colon, using a tab character, or casing it as Transfer-encoding. This is where header parsing inconsistencies across layers cause real security failures, not just interoperability annoyances.

Crafting a CL-TE Payload: A Concrete Example

The following is an illustrative payload β€” not a weaponized exploit β€” to show exactly what bytes are involved.

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

0

GET /admin HTTP/1.1
X-Ignore: x

Breaking this down:

  • Front-end reads: Content-Length: 35. It counts 35 bytes starting after the blank line between headers and body, then forwards the whole thing.
  • Back-end reads: Transfer-Encoding: chunked. The first chunk size is 0, which means end-of-body. The back end processes the POST as complete. Everything after the 0\r\n\r\n β€” the partial GET request β€” is left in the socket buffer.
  • Next legitimate request arrives: The back end prepends the leftover bytes to it. The back end sees GET /admin HTTP/1.1\r\nX-Ignore: xGET /real-path HTTP/1.1\r\n…. Depending on the application, this may bypass access controls or cause the victim's request to be mis-routed.

The exact byte count in Content-Length matters. You need to count the bytes after the header section accurately, including \r\n line endings. Off-by-one errors will cause a 400 rather than a successful smuggle.

Detecting CL-TE Vulnerabilities

Timing-based detection

The simplest detection technique exploits the fact that a back end waiting for more chunked data will hang until a timeout. Send a request with a non-zero chunk size that you never complete:

POST / HTTP/1.1
Host: target.example.com
Content-Length: 4
Transfer-Encoding: chunked

1
A

If the front end uses Content-Length, it reads 4 bytes (1\r\nA) and forwards immediately. The back end, reading chunked, gets a chunk of size 1, receives A, and then waits for the next chunk that never comes β€” causing a noticeable delay. A response time significantly longer than your baseline (typically 10+ seconds before a timeout) is a strong signal that CL-TE desync is present.

Differential response detection

Send two requests in quick succession. The first request smuggles a partial prefix; the second is a normal request. If the second request gets an unexpected response β€” such as a 404 for a path that should 200, or content belonging to the smuggled prefix β€” the server is vulnerable.

Burp Suite's HTTP Request Smuggler extension (by James Kettle) automates both timing and differential detection. Point it at your host, run the active scan, and it will tell you which variant (CL-TE, TE-CL, TE-TE) is present. Be careful running this against production: it actively mutates requests and can corrupt legitimate sessions during testing.

Manual confirmation with a safe canary

To confirm without impacting real users, use a unique path that only your test account accesses, and set the smuggled prefix to prepend a custom header to the next request. Then send a follow-up request and check whether the back-end response reflects the injected header. This is a non-destructive proof-of-concept approach.

Blocking CL-TE Desync Attacks

Normalize headers at the front end

The most effective fix is to make the front-end proxy rewrite incoming requests so both Transfer-Encoding and Content-Length are never present simultaneously in the forwarded request. Your proxy should strip Content-Length when Transfer-Encoding: chunked is present, or vice versa, before forwarding.

In Nginx acting as a reverse proxy:

proxy_http_version 1.1;
proxy_set_header Transfer-Encoding "";

This tells Nginx to use HTTP/1.1 for upstream connections and strip any Transfer-Encoding header the client sent, forcing requests to be forwarded with only Content-Length. This eliminates the ambiguity entirely for the upstream.

For HAProxy, set option http-server-close or option forceclose to disable connection reuse, which removes the socket-buffer-sharing mechanism that smuggling relies on:

backend app_servers
    option http-server-close
    server app1 127.0.0.1:8080

Reject ambiguous requests at the edge

Configure your front-end to return a 400 Bad Request for any request that contains both Content-Length and Transfer-Encoding. This is the strictest and safest option. Legitimate clients and well-behaved frameworks never send both headers simultaneously.

In Nginx with the ngx_http_limit_req_module or a WAF rule, you can reject such requests. Many WAF products (including AWS WAF and Cloudflare WAF) ship with managed rules that flag ambiguous header combinations β€” enable those rules if you use a WAF layer.

Upgrade to HTTP/2 end-to-end

HTTP/2 frames have explicit length fields in the binary protocol; there is no Transfer-Encoding: chunked in H2. If you can run HTTP/2 between your front end and back end (not just client-to-edge), the CL-TE and TE-CL vectors disappear because the ambiguity in HTTP/1.1 framing no longer exists.

Note that HTTP/2 introduces its own class of desync attacks via H2-to-H1 downgrade, where the edge speaks H2 to clients but H1 to the origin. That's a separate problem, but worth factoring into your architecture decisions. Understanding the full scope of protocol-level parsing vulnerabilities helps you prioritize the right fixes.

Disable connection reuse on the back end

Smuggling only works because the front end and back end share a persistent connection. If you set the back-end connection to close after every request (Connection: close on forwarded requests), each request gets a fresh TCP connection, and there is no shared buffer to smuggle into. The trade-off is performance β€” keep-alive connections are faster β€” so reserve this for high-risk internal endpoints if you can't patch the header handling.

Common Pitfalls When Patching

Stripping only the standard header casing. Attackers use obfuscated variations like Transfer-Encoding: chunked with trailing whitespace, tab-separated values, or unexpected casing. Your stripping logic needs to be case-insensitive and handle all valid (and many invalid) header syntax variants. Test with Transfer-encoding, TRANSFER-ENCODING, and headers with leading/trailing spaces.

Fixing the proxy but not the CDN. If you have a CDN in front of your reverse proxy, you now have two proxy layers. The desync can occur between the CDN and your proxy, not just between your proxy and the origin. Each layer transition needs to be hardened. This is similar in spirit to the multi-layer trust issues found in OAuth redirect URI misconfigurations, where each intermediary makes its own trust decision.

Only testing with safe methods. GET requests can also carry bodies in HTTP/1.1, and some proxies handle them differently from POST. Test your detection with both GET and POST with bodies.

Assuming your WAF catches it all. WAF rules block known patterns, but smuggling payloads are highly variable. A WAF is a useful layer but not a substitute for fixing the underlying header normalization at the proxy level.

Forgetting internal services. Internal microservices behind an internal load balancer are just as vulnerable. Attackers who gain access to an internal network segment can use smuggling to pivot between services. Treat internal traffic with the same header normalization discipline as public-facing traffic. This connects to a broader point about internal service reachability and why network position alone doesn't guarantee safety.

Wrapping Up: Next Steps

CL-TE desync is a well-understood vulnerability with clear, actionable mitigations. The attack surface exists because HTTP/1.1 has two competing ways to describe body length, and different implementations pick different winners. Remove that ambiguity and the attack falls apart.

  • Audit your proxy configuration today: check whether your front-end strips or normalizes conflicting Content-Length/Transfer-Encoding headers before forwarding.
  • Run Burp Suite's HTTP Request Smuggler against a staging environment to confirm whether you're vulnerable. Don't skip this even if you think you've patched β€” verify.
  • Enable option http-server-close in HAProxy or equivalent keep-alive disabling on your back-end connections as an immediate stopgap while you harden header normalization.
  • Plan an HTTP/2 end-to-end upgrade on your internal service mesh if your infrastructure supports it β€” this eliminates the entire class of H1 framing ambiguity attacks.
  • Add a regression test to your CI pipeline that sends a request with both headers and asserts a 400 response, so regressions surface before they reach production.

Frequently Asked Questions

What is the difference between CL-TE and TE-CL request smuggling?

In CL-TE, the front-end proxy prioritizes Content-Length while the back-end server prioritizes Transfer-Encoding, allowing the attacker to append a prefix to the next request the back end processes. In TE-CL it is the reverse: the front end follows Transfer-Encoding and the back end follows Content-Length, requiring a different payload structure to achieve the same desync effect.

Can HTTP/2 completely prevent request smuggling attacks?

HTTP/2 eliminates CL-TE and TE-CL vectors because its binary framing protocol has no Transfer-Encoding chunked mechanism. However, if your edge speaks HTTP/2 to clients but downgrades to HTTP/1.1 when talking to the origin (H2-H1 downgrade), a separate class of HTTP/2 desync attacks can still apply.

Does disabling keep-alive connections stop HTTP request smuggling?

Yes, disabling persistent connections between the front-end proxy and back-end server removes the shared socket buffer that smuggling depends on. Without connection reuse, there is no leftover buffer from one request to prepend to the next, so the attack mechanism breaks entirely β€” though the performance trade-off can be significant at scale.

How do I safely test for CL-TE smuggling in production without affecting real users?

Use the timing-based technique first: send a request with an incomplete chunked body and measure the response delay. This is mostly safe because it only affects your own connection. Avoid differential response testing in production since it involves smuggling prefixes that can corrupt a real user's request in the same connection pool.

Why does obfuscating the Transfer-Encoding header help attackers bypass front-end detection?

Some proxies apply case-sensitive or whitespace-strict matching when deciding whether to process a Transfer-Encoding header. A header written as 'Transfer-encoding' with a tab or trailing space may be ignored by the front-end proxy but still parsed and honored by the back-end server, recreating the disagreement that makes smuggling possible.

πŸ“€ Share this article

Sign in to save

Comments (0)

No comments yet. Be the first!

Leave a Comment

Sign in to comment with your profile.

πŸ“¬ Weekly Newsletter

Stay ahead of the curve

Get the best programming tutorials, data analytics tips, and tool reviews delivered to your inbox every week.

No spam. Unsubscribe anytime.