Header · not registered
< Link: </app.css>; rel=preload; as=styleEarly-Hints
Learn how 103 Early Hints allows servers to send preload hints while preparing the main response. Improve page load performance with early resource loading.
- Direction
- Response
- Category
- Preloading
- Spec
- RFC 8297
- Status
- Not a real header: 103 Early Hints is a status code; it carries Link headers
On this page
TL;DR: Early Hints is the
103status, not a header. The server sends it while it’s still building the page, withLinkheaders that tell the browser to preload a stylesheet or preconnect to an origin. Browsers can ignore 103, so repeat the same links in the final response or HTML.
What is Early-Hints?
Server think time is wasted time for the browser. RFC 8297 defines 103 to fill that gap: the server sends some headers early, and the browser can start fetching a hinted stylesheet before the HTML arrives. A 103 has no body, and it doesn’t commit the server to anything. The final response can have a different status and different headers.
Example
Here’s a trimmed HTTP/1.1 exchange where the connection closes after the HTML:
HTTP/1.1 103 Early Hints
Link: </style.css>; rel=preload; as=style
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Link: </style.css>; rel=preload; as=style
Connection: close
<!doctype html><link rel="stylesheet" href="/style.css"><p>Hello</p>
The blank line ends the 103. The HTML belongs to the final 200, which repeats the Link header for clients that skipped the 103.
What to Send in 103
Hint resources you know every response for that page needs, before you’ve generated it: the main stylesheet, a critical font, the origin your API lives on. For a stylesheet, as=style tells the browser what it’s fetching. Font preloads need crossorigin to match the CORS fetch the page will make later, even for same-origin fonts. Check that each hint gets used before adding more.
Implementation
On Cloudflare Workers, you don’t build the 103 yourself. Enable Early Hints for the zone and return preload links in the final response, as in Cloudflare’s Worker example:
export default {
async fetch() {
return new Response(
'<!doctype html><link rel="stylesheet" href="/style.css"><p>Hello</p>',
{
headers: {
'Content-Type': 'text/html; charset=utf-8',
Link: '</style.css>; rel=preload; as=style',
},
},
);
},
};
Serve /style.css separately. Cloudflare caches the Link headers from this response and uses them to send 103s on later requests, so the request that sets the header won’t necessarily get a 103 itself.
In Node, response.writeEarlyHints() sends the 103 directly. This server sends the hint, then reads the page from disk, and also serves the stylesheet it hinted:
import http from 'node:http';
import { readFile } from 'node:fs/promises';
http.createServer(async (req, res) => {
if (req.url === '/style.css') {
res.writeHead(200, { 'Content-Type': 'text/css' });
res.end('body { font-family: sans-serif; }');
return;
}
if (req.url !== '/') {
res.writeHead(404);
res.end();
return;
}
const link = '</style.css>; rel=preload; as=style';
res.writeEarlyHints({ link });
try {
const html = await readFile(new URL('./index.html', import.meta.url));
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', Link: link });
res.end(html);
} catch {
res.writeHead(500);
res.end();
}
}).listen(8084, '127.0.0.1');
Save it as hints-demo.mjs next to an index.html that links /style.css, then run node hints-demo.mjs. curl -sS -D - http://127.0.0.1:8084/ prints both response blocks. Delete index.html and you’ll see a 103 followed by a 500: the hint goes out before the server knows whether the page will work.
Implementation Notes
Cloudflare’s zone implementation only sends 103 over HTTP/2 and HTTP/3, never HTTP/1.1. It handles preload and preconnect links, and skips the 103 when the final response is already available. It only generates hints for URIs ending in .html, .htm, .php, or with no extension, where the final status is 200, 301, or 302.
Cloudflare Pages has a separate integration that builds hints from <link> elements in your HTML. A Cloudflare zone reads Link headers instead, so check what your CDN actually looks at.
For browser support, see MDN’s compatibility table.
Common Mistakes
The most common mistake is sending an Early-Hints: ... header, which does nothing. The second is putting Link only in the 103, so clients that ignore 103 never see the hint. Also, Cloudflare keys hints by URI without the query string and can send them before authentication runs, so keep sensitive URLs out of hints that every visitor shares.
To inspect the edge response, use an HTTP/2-capable curl build:
curl --http2 -sS -D - -o /dev/null https://example.com/
Look for a 103 header block before the final status. Then open the browser’s Network panel to confirm the hinted resource was fetched early and what started the fetch.
Early Hints vs HTTP/2 Server Push
Early Hints sends only metadata, and the browser decides whether to fetch. HTTP/2 server push sent the resource itself on a separate stream, whether or not the browser already had it cached. With 103 you only win if a fetch the page needs starts earlier than it otherwise would.
Related Headers
Frequently asked questions
What are Early Hints?
Early Hints is the 103 status code, an interim response that carries Link headers before the real response is ready. There is no Early-Hints header.
How do Early Hints improve performance?
While the server is still building the page, the browser can start fetching hinted resources such as stylesheets. How much time that saves depends on the page, the resource and the browser, so measure it.
Which browsers support Early Hints?
Check the current MDN compatibility table, and test the specific hint type you rely on. A browser that supports 103 may act on preconnect hints but not preload.
How do I implement Early Hints?
Use a server or CDN that can emit 103. In Node, call response.writeEarlyHints(). Cloudflare zone Early Hints builds hints from Link headers on your responses, while Cloudflare Pages has a separate integration that reads HTML link elements.
Sources
Related
Access-Control-Max-Age Header
Learn how Access-Control-Max-Age specifies how long browsers can cache CORS preflight results. Reduce preflight requests and improve cross-origin performance.
Content-Encoding
Learn how Content-Encoding specifies compression algorithms (gzip, br, deflate) used to encode response bodies. Reduce bandwidth and improve load times.
Link Header
Learn how the Link header provides resource hints and enables preloading of CSS, fonts, and scripts to improve page load performance and user experience.
Performance-Timing: Server-Timing, Timing-Allow-Origin and Performance APIs
There is no Performance-Timing HTTP header. Use Server-Timing, Timing-Allow-Origin, the Navigation and Resource Timing APIs, TTFB and INP instead.