v154 · network · webtransport

WebTransport headers and responseHeaders

A WebTransport session opens with an HTTP/3 CONNECT, and that handshake could carry headers all along — the API just gave you nowhere to put them. Chrome 154 adds headers to WebTransportOptions and exposes the server's reply as responseHeaders, so authentication and negotiation can happen at connect time instead of in the first datagram.

concepts

  1. Reading the option

    Hand the constructor getters that throw and the options bag reports its own contents, member by member — with two control rows that make the answer trustworthy. Then read the prototype for the response half.

  2. What the handshake carries

    Type headers and watch each one be sent, dropped silently, or rejected outright — decided by building a real Request, which is where the forbidden-name rules actually live.

  3. Authenticating at connect time

    The pattern this replaces, measured for real against this server: credentials in the handshake versus credentials in the first message, timed, with the window during which the server holds a session for a peer that has proven nothing.

why it shipped

Without headers, a WebTransport session had no way to say who it was before it was open. The workaround is universal and bad: connect anonymously, then send credentials as the first message, and have the server keep the session in a half-trusted state until they arrive. That means the server accepts and tracks connections it has no reason to trust, and the client discovers a rejected token one round trip later than necessary.

The CONNECT request is an HTTP request. Letting it carry headers means a bearer token, an API version, or a tenant identifier can be checked before a session exists at all — which is how every other HTTP-based transport already works. responseHeaders completes the pair: the server can answer with what it negotiated.

the API

const transport = new WebTransport("https://example.com:4433/session", {
  headers: { "authorization": "Bearer …", "x-protocol-version": "3" },
});
await transport.ready;

// What the server said in the CONNECT response.
for (const [name, value] of transport.responseHeaders) {
  console.log(name, value);
}

Forbidden header names are refused, the same set fetch() refuses — a page cannot forge Host, Cookie or Origin here any more than it can there.

references