103 Early Hints (tested)

103 Early Hints is an informational response a server sends before the real one, usually carrying Link: rel=preload or rel=preconnect headers so the browser can start fetching CSS, fonts or scripts while the server is still building the page. We sent real 103s from Node.js 24 over HTTP/1.1 and HTTP/2, delayed the 200 by one second, and logged exactly when each client asked for the preloaded file.

1. Sending a 103 from Node.js

Node.js 24.21.0 has response.writeEarlyHints() on both http and http2 servers. Our test handler sent the hint, waited 1,000 ms (standing in for a slow database query), then sent the page:

function handler(req, res) {
  if (req.url === '/') {
    res.writeEarlyHints({ link: '</style.css>; rel=preload; as=style' });
    setTimeout(() => {
      res.writeHead(200, { 'content-type': 'text/html' });
      res.end('<!doctype html><link rel=stylesheet href=/style.css><p>hi');
    }, 1000);
  }
}

The same handler worked unchanged with http.createServer and http2.createSecureServer.

2. What curl shows

curl -v http://127.0.0.1:8983/ printed two complete header blocks on one request:

< HTTP/1.1 103 Early Hints
< Link: </style.css>; rel=preload; as=style
<
< HTTP/1.1 200 OK
< content-type: text/html
< Connection: keep-alive
< Transfer-Encoding: chunked

Over HTTP/2 (curl -kv --http2 https://127.0.0.1:8984/) it was HTTP/2 103 with a lowercase link: header, then HTTP/2 200. Note the 103 carries only the headers you pass to writeEarlyHints; it has no body and does not replace the final response. If you only look at the last header block (for example with curl -I output piped to grep), you will not notice the 103 at all.

3. What Node.js clients expose

ClientSees the 103?Measured
http.getYes, as an 'information' eventinformation 103 { link: '</style.css>; rel=preload; as=style' } at 16 ms, response 200 at 1,013 ms
Built-in fetch()NoResolved once with status 200 at 1,016 ms; no link header in the result
http.get(url, res => { /* the 200 */ })
  .on('information', info => console.log(info.statusCode, info.headers));

If you need to test that your server really sends Early Hints, use curl -v or http.get with the 'information' listener. fetch() silently skips 1xx responses.

4. Does Chromium act on it? HTTP/1.1 vs HTTP/2

We loaded the page in headless Chromium 153 and logged, on the server, when /style.css was requested relative to the page request. The 200 was held back for 1,000 ms, so a CSS request well under 1,000 ms can only have come from the 103.

ProtocolServer saw /style.css atActed on the 103?
HTTP/1.1, http://127.0.0.1:89831,018 ms (once, after the HTML arrived)No
HTTP/2, https://localhost:8984 (self-signed, ignoreHTTPSErrors)14 ms, then again at 1,018 msYes, preload started ~1 s before the HTML

This matches Chromium's documentation (from the docs, not measured beyond this test): Chrome only processes Early Hints on HTTP/2 or HTTP/3 navigations, and ignores them on HTTP/1.1. A plain HTTP/1.1 origin gets no benefit, so measure through the protocol your users actually get (often HTTP/2 or HTTP/3 at a CDN edge).

Two things we saw but did not dig into:

5. Practical rules

Frequently asked questions

What is HTTP 103 Early Hints?

An informational status the server sends before the final response, usually with Link preload or preconnect headers. In our Node.js 24 test the 103 arrived at 16 ms and the 200 at 1,013 ms on the same request.

Does Chrome support 103 Early Hints over HTTP/1.1?

No. Chromium 153 ignored our 103 on HTTP/1.1 and fetched the stylesheet only after the HTML arrived (1,018 ms). Over HTTP/2 it requested the stylesheet at 14 ms, about one second before the HTML.

How do I see a 103 response with curl?

Use curl -v. It prints the HTTP/1.1 103 Early Hints block with its Link header, then the 200 block. Both appear for a single request.

Why does fetch() not show the 103?

Node.js 24 fetch() skips 1xx responses and resolves only with the final 200. Use http.get and listen for the 'information' event to read the 103 and its headers.

How do I send Early Hints in Node.js?

Call res.writeEarlyHints({ link: '</style.css>; rel=preload; as=style' }) before res.writeHead. It worked unchanged on both http.createServer and http2.createSecureServer in Node.js 24.21.0.

Written with AI assistance. Measured on 2026-10-02 with Node.js 24.21.0 (http.createServer on localhost:8983, http2.createSecureServer with a self-signed cert on localhost:8984, response.writeEarlyHints, a 1,000 ms delay before the 200), curl 8.14.1, Node's http.get and built-in fetch, and headless Chromium 153.0.8010.12 (Playwright). We logged the time of every request the server received. Chromium's HTTP/2-only rule and the CDN notes are from the docs, not measured. PlainHeaders is our extension; Node.js, curl and Chromium are free.