RELAYD-001 — OpenBSD relayd: CL.TE HTTP request smuggling via co-present Content-Length and Transfer-Encoding

relayd parsed the body using Transfer-Encoding: chunked but did not remove a co-present Content-Length header before forwarding to backend, contrary to RFC 9112 §6.1. A single crafted HTTP request makes the proxy and the backend disagree on the message boundary, enabling request smuggling. Latent in relayd since OpenBSD 5.2 (2012); fixed in −current on 2026-06-03.

Stuart Thomas

Independent Security Research (TriageForge) — Whitby, North Yorkshire, United Kingdom

4 June 2026  ·  Product: OpenBSD relayd (HTTP forwarding)  ·  Class: CWE-444 — Inconsistent Interpretation of HTTP Requests  ·  Severity: Remote HTTP request smuggling — security-control bypass against backends  ·  ORCID: 0009-0008-4518-0064  ·  CC BY 4.0


1. Summary

OpenBSD’s relayd is a daemon for reverse-proxying and load-balancing TCP, HTTP and HTTPS traffic to backend services. When forwarding an HTTP message whose framing uses Transfer-Encoding: chunked, relayd correctly parses the body as a chunked stream — but it does not remove a co-present Content-Length header before passing the message to the backend. This is contrary to RFC 9112 §6.1, which requires a sender to remove the Content-Length field from any message that also carries Transfer-Encoding.

A remote, unauthenticated attacker can therefore send a single HTTP request containing both Content-Length and Transfer-Encoding: chunked framing such that relayd and the backend server disagree on where the request body ends. This is the textbook CL.TE variant of HTTP request smuggling: the front-end (relayd) uses chunked, the back-end uses Content-Length, and the gap between the two interpretations becomes a smuggled second request prepended to whatever HTTP traffic follows on the same connection.

The practical impact, on any deployment where relayd sits in front of an HTTP backend that prefers Content-Length when both headers are present, is the standard CWE-444 set: security-control bypass (smuggling past WAF rules, path-based ACLs, or authentication checks that the front-end enforces), request-routing manipulation, and any downstream consequences the smuggled request can produce against the backend (cache poisoning, session-stealing on shared connections, etc., depending on the backend’s posture).

The defect was latent in relay_http.c from OpenBSD 5.2 onward — the release that introduced the current HTTP forwarding code in relayd — and was fixed in −current on 2026-06-03 in commit e8e5aa2db9cf7bcd254dadc5f14a69547006e9a2.

2. Technical detail

File: usr.sbin/relayd/relay_http.c — HTTP forwarding code path.

Standard at issue: RFC 9112 (HTTP/1.1, June 2022), §6.1 “Transfer-Encoding”:

“A sender MUST NOT send a Content-Length header field in any message that contains a Transfer-Encoding header field.”

RFC 9112 §6.1

The conformant behaviour for any HTTP proxy that handles a message with both headers is either to reject the request outright, or to strip the Content-Length header before forwarding (relying on the chunked framing it has already chosen to honour). Anything else creates the conditions under which a backend that prefers Content-Length will disagree with the proxy about the message boundary, and the proxy / backend mismatch is the smuggling primitive.

2.1 — The CL.TE primitive

The CL.TE variant of HTTP request smuggling is the case where the front-end uses Content-Length and the back-end uses Transfer-Encoding, or vice versa. Either disagreement turns the bytes that one party considers "end of request 1 / start of request 2" into bytes the other party considers "still inside request 1’s body". The attacker controls those bytes, and the controlled bytes become the prefix of whatever the back-end parses as the next request — smuggled past every check that the front-end performed on request 1.

In this particular case the disagreement goes the other way: relayd is the front-end and it uses Transfer-Encoding. The backend, where it prefers Content-Length (which many backends do when both headers are present), uses Content-Length. The smuggling primitive is identical in effect.

The exact bytes-on-the-wire required to exploit any given (relayd + backend) combination depend on the backend’s exact framing preference. Detailed exploit payloads are deliberately out of scope for this disclosure; the disclosure’s purpose is to allow operators to patch, not to ship a working smuggling crowbar.

2.2 — Why this has been latent for thirteen years

HTTP request smuggling as a class was first systematised in 2005 (Linhart, Klein, Heled and Orrin, published by Watchfire) and then popularised across the industry by PortSwigger’s 2019 research, after which most front-end proxies were audited and updated. relayd’s HTTP forwarding code dates from OpenBSD 5.2 (November 2012), and the absence of a Content-Length strip when Transfer-Encoding is present has been latent in relay_http.c since that release. The defect is the kind that quiet, internally-used proxy code can carry for a long time without anyone running the specific RFC-conformance test that catches it — particularly when the deployment patterns for relayd have historically leaned toward simple TLS-termination-in-front-of-OpenBSD-services rather than fronting third-party backends with quirky header parsers.

That is not an excuse for the defect having survived this long; it is an explanation for the survival pattern. relay_http.c is reasonably small and the relevant code path is reachable on any HTTP relay configuration. A targeted source-review pass against the RFC 9112 framing rules surfaced it.

3. Affected versions

Deployments at risk are those where relayd is operating in HTTP-forwarding mode (rather than pure TLS termination or layer-4 TCP relay) and forwards traffic to a backend HTTP server that prefers Content-Length over Transfer-Encoding when both headers are present. The specific list of backends that have that preference is implementation-dependent and outside the scope of this disclosure; operators should assume risk by default unless their backend is known to reject conflicting framing per RFC 9112.

4. Fix

Fixed in OpenBSD −current, commit e8e5aa2db9cf7bcd254dadc5f14a69547006e9a2, on 2026-06-03.

The fix lives in usr.sbin/relayd/relay_http.c and is small: when relayd determines that an incoming HTTP message uses Transfer-Encoding: chunked framing, the Content-Length header is now stripped from the outbound message before forwarding to the backend, per RFC 9112 §6.1.

The full commit (including any additional defensive-rejection logic) is browsable upstream:

5. Fix availability

The fix is in OpenBSD −current as of 2026-06-03 (commit e8e5aa2db9c), verifiable directly against the openbsd/src mirror at the cited hash.

Release timing and any backport to the supported −stable branches are the OpenBSD project’s call. The practice does not speculate on which release will include this commit or on whether it will ship as −stable errata against the supported branches. The authoritative sources are:

Interim posture for operators running relayd in HTTP forwarding mode on OpenBSD 7.9 or earlier:

6. Timeline

DateEvent
2012-11OpenBSD 5.2 released; current relay_http.c HTTP forwarding code present in this form. Defect latent from here onward.
2026-05-28Reported to bugs@openbsd.org.
2026-06-03Fixed in OpenBSD −current in commit e8e5aa2db9c.
2026-06-04This disclosure published.

7. Credit

Found independently by Stuart Thomas (TriageForge) during a targeted source-review pass of relay_http.c against the RFC 9112 framing rules. Reported to the OpenBSD project and acknowledged via the upstream commit.

The OpenBSD project is, as in earlier disclosures listed on this site, named here in its public capacity as the maintainer of the affected software. Individual developers involved in the fix are credited only in their public capacity as committers and only in connection with their own public commits; their handling of the report was prompt and professional, consistent with this author’s experience of the project across the 2026 disclosure batch.

This disclosure is published under the Defamation Act 2013 facts-and-opinion convention. Statements of fact — commit hashes, dates, the cited RFC text, file paths, and the historical release of relay_http.c — are accurate to the best of the author’s knowledge and are evidenced by the OpenBSD CVS commit log, the public github.com/openbsd/src mirror, and RFC 9112 as published by the IETF. The verbatim text of bugs@openbsd.org correspondence is not reproduced here; paraphrasing in §6 preserves the substance.

The OpenBSD project is named as the publicly-accountable maintainer of the affected software. Individual developers are named only in their public capacity as committers and only in connection with their own public commits.

The research was conducted on hardware owned by the author. No third-party systems were accessed in the course of the research; the smuggling primitive was reasoned out from the source code and the RFC and was not exercised against any live deployment outside the author’s own test environment. The work was performed within the scope permitted by the Computer Misuse Act 1990 (England and Wales) own-hardware exemption.

Detailed exploit payloads are intentionally not published. The disclosure’s purpose is to allow operators to patch — not to ship a working smuggling crowbar against unpatched deployments. Coordinated disclosure of vulnerabilities in network-facing infrastructure software is a matter of legitimate public interest under s.4 of the Defamation Act 2013, and this disclosure is published in good faith on that basis.

Where any fact has been described inaccurately, the author will correct it; please email stuart.thomas@triageforge.co.uk.