The Semantic Contract at the Edge

At the HTTP/3-to-HTTP/1.1 boundary, the important question is not whether a CDN can decode a newer protocol. It is whether the CDN can carry a request's authority, limits, body, and boundaries into the older representation without creating a second interpretation. This chapter follows the fields and framing rules that make that judgment possible, giving readers a concrete contract for understanding the conversion behavior measured by H3Act.

The word “translation” can make the operation sound mechanical, like changing one file format into another. An HTTP request is more than a format, though. Its fields determine which resource is addressed, how much content is requested, whether the origin should be contacted, and where one message ends. A translator that changes any of those meanings has changed the transaction even if the client receives a plausible status code.

What travels on an HTTP/3 request stream

HTTP/3 carries HTTP semantics over QUIC. QUIC supplies a connection, but HTTP/3 assigns individual request-response exchanges to client-initiated bidirectional streams. Within one request stream, the request begins with a HEADERS frame and may include DATA frames for content. A response returns on that stream with response headers and, when needed, DATA. The stream's closure marks the end of the final HTTP message.

That structure solves problems that were difficult to solve with a single ordered TCP byte stream. Multiple HTTP/3 streams can make progress independently, so a delay affecting one transaction need not stop every other transaction. Flow control can also be applied per stream. These are transport and multiplexing properties. They govern delivery and resource coordination; they do not choose the origin that should receive a request.

The request fields supply that application-level meaning. :method identifies the operation. :scheme identifies the URI scheme. :authority carries the authority portion of the target URI, and :path carries its path and query. The fields are not decorative labels attached to a connection. They belong to the individual request and tell an intermediary what the client is asking for.

HTTP/3's framing layer also makes the request boundary explicit. A normal exchange has a header section, optional content in DATA frames, and an optional trailer section. A client sends only one request on a given request stream. A DATA frame cannot appear before the request headers, and additional headers or data after the trailing headers are not valid. These rules give the edge a definite sequence to validate before it forwards anything.

The origin may not see any of that wire structure. On the other side of the CDN, an HTTP/1.1 server expects a different representation: a request line, fields such as Host, and a body whose boundaries are determined by HTTP/1.1 rules. The translator must therefore reconstruct a message from the HTTP/3 fields and frames. It is not supposed to invent new intent; it is supposed to express the existing intent in a form the next parser can consume.

This distinction also explains why connection state and request state must not be casually mixed. HTTP/3 settings apply to the connection as a whole, while :authority, :path, and the request body describe one stream's transaction. A connection can carry many requests, but it does not follow that the target of the first request is the target of every later request. State that is genuinely connection-wide can be reused. A target or limit that belongs to one request must travel with that request.

The timing of validation matters as well. HTTP/3 permits an endpoint to begin processing a partial message once enough has arrived to make progress, but that does not give an intermediary permission to forward an incomplete or malformed interpretation. The edge has to know which decisions are safe before the complete request arrives and which require the whole header section or body. A routing choice based on an early, connection-level signal may be stable for the transport and still be wrong for the request. A body-length choice made before all DATA frames arrive may reserve too little or too much space downstream. The boundary is therefore both spatial and temporal: the translator must preserve who the request is for and when its contents become authoritative.

There is a further reason to keep the boundary visible: a CDN can complete some work without contacting the origin. A cache hit may produce a response at the edge. A cache miss may initiate a back-to-origin request. A revalidation may require a different exchange. The client sees the edge's response, but the origin sees only the requests the CDN chooses to forward. A client-side trace alone cannot establish that the origin received the same authority, conditions, or body that the client intended.

What the translator must preserve

RFC 9114 makes the authority mapping unusually clear. A client that constructs HTTP/3 directly should use :authority rather than a Host field. When an intermediary converts an HTTP/3 request to HTTP/1.1, it must create Host from :authority if a Host field is not already present. For an ordinary HTTP or HTTPS request, the authority cannot be empty; if both :authority and Host are present, they must contain the same value. The conversion is correct only when the origin-facing identity remains the identity named by the request.

The :path field must be carried with equal care. For HTTP and HTTPS requests it cannot be empty, and it contains the path and query rather than the authority. Treating a path that happens to contain an absolute URI as interchangeable with the request's authority would collapse two different pieces of request meaning. The edge may use both to route or cache, but it must not allow them to silently describe different destinations.

Framing constraints matter just as much as names. HTTP/3 does not define transfer codings, so a Transfer-Encoding field must not be used. For a message that has content, a Content-Length value must equal the sum of the DATA-frame lengths received. The rule is not merely a bookkeeping preference. A downstream HTTP/1.1 parser may use length information to determine where the request body ends and where the next request begins. If the edge exports inconsistent length or framing information, it can make the origin see a different sequence of messages.

A sound conversion path can be understood as a sequence of gates. First, the edge establishes that the incoming HTTP/3 message has the required fields and a permitted order. Next, it resolves the request's effective authority and path, rather than selecting a backend from a convenient earlier signal. It then accounts for the content represented by the DATA frames and preserves any request condition that controls whether origin work is needed. Only after those meanings are fixed should it serialize the HTTP/1.1 request. The order is important: a serializer cannot repair a routing decision that was made from the wrong host, and a cache cannot retroactively restore a condition that the translator discarded.

These gates also give operators a way to describe a failure precisely. “The CDN accepted the request” says only that the first gate allowed it through. It does not say that the request reached the intended backend, that the origin saw the same body, or that a shared cache associated the response with the right resource. A conversion test should record the input, the edge decision, the forwarded representation, and the origin result as separate observations. That separation is what turns a protocol rule into an auditable contract.

sequenceDiagram participant C as Client participant E as CDN edge participant T as Translator participant O as HTTP/1.1 origin C->>E: HEADERS with request fields C->>E: Optional DATA frames E->>E: Validate fields and framing alt Valid request E->>T: Preserve request meaning T->>O: HTTP/1.1 Host, path, fields, and body O-->>T: HTTP/1.1 response T-->>E: HTTP/3 response semantics E-->>C: Response on the request stream else Malformed request E-->>C: Reject without forwarding end

The standard is deliberately strict about malformed input. Invalid or misplaced pseudo-header fields, missing mandatory fields, uppercase field names, invalid field characters, and invalid frame sequences are all reasons to treat a message as malformed. An intermediary that processes HTTP must not forward such a request or response. The strictness is a security control: accepting an ambiguous value at the newer edge and passing it to a more permissive older parser merely moves the ambiguity to a less visible part of the system.

Field values deserve particular attention because their danger may not be apparent at the HTTP/3 edge. RFC 9114 calls out carriage return, line feed, and the null character as values that could be exploited if translated verbatim. A value that is just a sequence of bytes to one layer may be a delimiter or line ending to another. The correct response is not to hope that all downstream parsers make the same choice. It is to reject a value that the HTTP/3 rules prohibit before it becomes an HTTP/1.1 message.

The same principle applies to any request-scoped constraint carried in the fields. A body-length declaration tells the edge and the origin how much content belongs to the transaction, while the frame sequence tells them where that content begins and ends. The translator does not get to discard that meaning because the new wire format expresses it differently. If a length or boundary changes during conversion, the origin may process a different message even when the client-visible response looks normal.

Preservation does not mean that every component must retain the exact bytes the client sent. Translation necessarily changes syntax. Header compression is decoded; pseudo-headers may become a request line or an ordinary field; binary frame boundaries become an HTTP/1.1 message. What must remain stable is the security-relevant interpretation. A normalized representation is safe when the cache, router, translator, and origin all use the same normalized identity and boundaries. It is unsafe when one component normalizes a value and another component uses the unnormalized or contradictory value for a different decision.

This is also a question of state ownership. The translator owns the mapping between representations, but it should not silently become the authority for a resource merely because it saw the first request on a connection. The cache owns a representation under a key, but that key must be derived from the request identity that the router and origin recognize. The origin owns the application action, but it can make a safe decision only if the forwarded message has not smuggled in an extra boundary or removed a condition. Naming the owner of each decision makes hidden coupling easier to find during design review and testing.

That is why a response can conceal a different origin-side request. Suppose the edge returns a familiar status, or returns a cached representation without contacting the origin. The client can conclude only that the edge produced that response. To determine what a miss or revalidation caused upstream, an investigator must compare the incoming HTTP/3 request with the translated HTTP/1.1 request and the origin's response. H3Act uses this before-and-after view because semantic conversion errors are often invisible in the client-facing result.

The comparison is most useful when it asks one focused question at a time. Did the authority used for routing remain the request's authority? Did a declared body remain the same size? Did a condition remain present? Did an invalid field stop at the edge? Did the origin receive one message rather than a sequence created by a downstream parser? Each question ties an observation to a specific invariant instead of treating “the CDN behaved strangely” as a conclusion.

The resulting contract has two sides. At the HTTP/3 ingress, the edge must validate the message and refuse malformed input. During translation, it must carry request-scoped meaning into the HTTP/1.1 representation. At the consuming layers, the cache, router, and origin must not derive different identities from different signals. Transport security can protect the connection while all three layers still disagree about the request. Conversely, a fast path can remain fast when it performs the small checks that preserve the request's authority.

H3Act's study is concerned with what happens when those checks fail in real CDN paths. Its contribution is not a new definition of HTTP/3 framing; RFC 9114 already supplies that contract. The study shows why the contract must be tested after translation, where independently implemented components decide what the fields and frames mean. A request that passes the edge parser is only the beginning of the proof. The origin-facing representation must still express the same transaction.

References