HTTP 103 Early Hints: sending Link headers before the final response, and where clients must stop

What a 103 Early Hints response carries per RFC 8297 and RFC 9110, how far a client may act on the hints, what happens with several 103s, and why HTTP/1.1 needs care.

DevelopmentWebNetworking

Say building an HTML response takes a 300 ms database query. The CSS and JavaScript that page will load are known the moment the request arrives, yet the server can’t send so much as a Link header in the meantime. The final response’s header fields go out together with its status code, and until the query returns the server doesn’t know whether that status will be 200 or 404.

103 Early Hints, defined in RFC 8297, fills that gap. The server puts header fields that are likely to appear in the final response into an informational (1xx) response, sends it straight away, and then carries on with the slow work.

What the exchange looks like

This is the example from Section 2 of RFC 8297:

GET / HTTP/1.1
Host: example.com
HTTP/1.1 103 Early Hints
Link: </style.css>; rel=preload; as=style
Link: </script.js>; rel=preload; as=script

HTTP/1.1 200 OK
Date: Fri, 26 May 2017 10:02:11 GMT
Content-Length: 1234
Content-Type: text/html; charset=utf-8
Link: </style.css>; rel=preload; as=style
Link: </script.js>; rel=preload; as=script

<!doctype html>
[... body omitted ...]

The 103 has header fields and no body. A client can spot rel=preload in its Link fields and start fetching /style.css early. If it does, the timing looks roughly like this:

client                              server
  |---- GET / ----------------------->|
  |<--- 103 Early Hints (Link) -------|  sent as soon as the request arrives
  |                                   |  database query running
  |---- GET /style.css -------------->|  fetched early because of the hint
  |<--- 200 OK (HTML) ----------------|  final response once the query is done

The Link field syntax itself comes from Section 3 of RFC 8288: a <URI> followed by ;-separated parameters, with relative URIs resolved by the rules of RFC 3986. The preload relation and the as attribute belong to the W3C Preload specification that RFC 8297 references; this post doesn’t cover them.

A hint is only a hint

The sentence in Section 2 of RFC 8297 that matters most is that the 103’s header fields do not replace the header fields on the final response. Spelled out:

  • They must not change how the final response is processed (MUST NOT). A client may use them for performance work, such as fetching something early, but apart from that the final response has to be handled exactly as it would have been without the 103.
  • They must not be read as metadata about the 103 itself (MUST NOT). A Content-Type or any other field in a 103 doesn’t describe the 103; it previews the final response.
  • A missing field means nothing (SHOULD NOT infer). A server may put only some of the expected fields in a 103 (MAY), so a client shouldn’t take a field’s absence from the 103 as a sign that the final response won’t have it either.
  • The final response can differ. A server will usually repeat the 103’s fields in the final response, but the spec notes it might not, for instance when it learns before sending the final response that the hinted fields were wrong.

From the server’s side, this means any header field the client actually needs to apply has to be in the final response, even if it was already in a 103. A 103 announces fields early; it doesn’t apply them early.

Sending more than one 103

Like any 1xx response, 103 can arrive more than once before the final response. The spec gives two situations:

  1. The server sends another 103 whenever new information turns up during processing (MAY). It doesn’t need to repeat fields it has already sent, though it’s allowed to.
  2. A caching intermediary builds a 103 from the header fields of a stale cached response, then forwards the 103 and the final response the origin sends for the revalidation request.

A client may combine fields from several 103s in any way when predicting the final response. The second example in the spec shows a prediction that misses:

HTTP/1.1 103 Early Hints
Link: </main.css>; rel=preload; as=style

HTTP/1.1 103 Early Hints
Link: </style.css>; rel=preload; as=style
Link: </script.js>; rel=preload; as=script

HTTP/1.1 200 OK
Date: Fri, 26 May 2017 10:02:11 GMT
Content-Length: 1234
Content-Type: text/html; charset=utf-8
Link: </main.css>; rel=preload; as=style
Link: </newstyle.css>; rel=preload; as=style
Link: </script.js>; rel=preload; as=script

Of the three Link fields hinted across the two 103s, /main.css and /script.js show up in the final response, but /style.css has been replaced by /newstyle.css. If the client fetched /style.css early, that request was wasted. The spec doesn’t forbid this, so the less wasteful choice is to put only fields in a 103 that are quite likely to be in the final response too.

Rules every 1xx response follows

RFC 8297 took the general 1xx rules from RFC 7231, which was current at the time. RFC 7231 has since been obsoleted by RFC 9110, and the rules now live in Section 15.2:

  • A 1xx response ends with the end of its header section. It can’t contain content or trailers.
  • A client must be able to parse one or more 1xx responses before the final response, even if it wasn’t expecting any (MUST). A user agent may ignore 1xx responses it didn’t expect (MAY).
  • A proxy must forward 1xx responses unless it asked for them to be generated itself (MUST).
  • HTTP/1.0 defined no 1xx status codes, so a server must not send a 1xx response to an HTTP/1.0 client (MUST NOT).

The opening of Chapter 15 sums it up: one request can have zero or more interim 1xx responses followed by exactly one final response. Because a 103 must not affect final-response processing beyond performance, a user agent that doesn’t understand 103 and ignores it only has to handle the final response correctly. The trouble is a client that can’t even parse a 1xx properly.

The HTTP/1.1 risk

Section 3 of RFC 8297 (Security Considerations) addresses this head-on. Informational responses were rare on requests without an Expect header field, so some clients might not handle a 103 properly.

In particular, an HTTP/1.1 client that treats a 103 as the final response is likely to consider all responses to later requests on the same connection to be part of that “final response”. The response boundaries slip out of step. If the client multiplexes requests to different origins onto one persistent connection, this can become a cross-origin information disclosure vulnerability.

The spec therefore says a server might refrain from sending 103 over HTTP/1.1 unless it knows the client handles informational responses correctly. It adds that HTTP/2 clients are less likely to suffer from this kind of framing error, because handling response header fields there doesn’t affect how the end of the response body is determined.

Compared with server push

Section 1 of RFC 8297 also compares 103 with HTTP/2 server push. As of 2017 the spec named two limitations of push: it only works for resources the server is authoritative for, and the response is sent whether or not the client already has it cached. Sending Link header fields early with a 103 costs one extra round trip compared with push in the worst case, but the spec says it is more flexible and might use less bandwidth. Set against those two limitations, the reading is that the client decides whether to make the request, so it can skip resources already in its cache.

The same section notes that an origin server can also use a 103 to trigger HTTP/2 server push at a caching intermediary.

Where the spec stands, and what this post leaves out

RFC 8297 is an Experimental document, not a Standards Track one. It registered code 103 with the description “Early Hints” in the IANA HTTP Status Codes registry (Section 4).

This post relies only on RFC 8297, Chapter 15 of RFC 9110 and Section 3 of RFC 8288. Which browsers, CDNs or web servers support 103, and which fields or relations other than preload browsers actually act on, aren’t in those specs, so they aren’t covered here. Check the official documentation of the implementation you plan to use.

Sources

Links and content checked on 20 October 2026.

Back to the blog