Understanding HTTP 103 Early Hints: Faster Pages with Smarter Preloads
There's a window of time on every page request where the server is busy working — querying the database, rendering a template, calling an upstream API — and the browser is just sitting there, waiting. HTTP 103 Early Hints is the standard way to put that idle time to work. It's a small protocol feature with a surprisingly big payoff for perceived performance, and it's now supported across most major browsers, CDNs, and platforms. Let's break down what it is, when it helps, and how to ship it.
What Is a 103?
A 103 is an informational response. The server sends it before the real (final) response, with one job: tell the browser which resources it should start fetching now, while the server keeps working on the real reply.
The 103 (Early Hints) informational status code indicates to the client that the server is likely to send a final response with the header fields included in the informational response. — RFC 8297
In plain English: the server is giving the browser a sneak peek of what's coming so it can start downloading critical resources (CSS, JavaScript, fonts, hero images) immediately — instead of waiting until the HTML lands to discover them.
Crucially, a 103 is not a commitment. The final response can still be a 200, a 302, or even a 500. The hints are a hopeful prefetch, not a promise.
The Performance Problem It Solves
Every modern web page has a critical-path resource problem. The browser can't paint until it has the HTML and the CSS the HTML references. But the browser doesn't know about that CSS until it parses the HTML. Which it can't parse until the server sends it. Which it can't send until the server is done generating it.
So you get a waterfall like this:
Request → [server thinks for 200ms] → HTML → [browser parses] → CSS request → CSS → paint
▲ ▲
│ browser is idle │ now browser knows it needs CSS
That 200ms of "server is thinking" is dead air. The CDN can't help, HTTP/2 multiplexing can't help, the browser is just waiting.
103 lets you fill that dead air:
Request → 103 Link: /style.css → [server keeps thinking] → HTML → [parse] → paint
▲ ▲
│ browser starts fetching CSS NOW │ paint happens sooner
In practice, this can knock 100–500ms off Largest Contentful Paint on slow upstream pages. It's most valuable for pages where time-to-first-byte is the bottleneck — server-rendered apps, dynamic personalization, anything that calls a slow database.
Anatomy of a 103 Response
On the wire, a 103 looks like this:
HTTP/1.1 103 Early Hints
Link: </style.css>; rel=preload; as=style
Link: </app.js>; rel=preload; as=script
Link: <https://fonts.example/inter.woff2>; rel=preload; as=font; crossorigin
HTTP/1.1 200 OK
Content-Type: text/html
Link: </style.css>; rel=preload; as=style
Link: </app.js>; rel=preload; as=script
<!doctype html>
<html>...
Note two things:
- The server sends two response status lines on the same connection — the 103 first, then the final 200 (or whatever it ends up being).
- The
Linkheaders in the 103 use the same syntax asLink: rel=preloadheaders you might already use on a 200. The browser treats them identically — it just gets to start earlier.
103 vs. Other Preload Techniques
There are several ways to tell the browser to preload a resource. Each has its place:
| Technique | Where it lives | When the browser learns about it | Best for |
|---|---|---|---|
<link rel="preload"> | In the HTML <head> | After HTML starts streaming | Resources discovered late in the document |
Link: header on 200 | HTTP response headers | When the 200 lands | Resources known statically per-route |
| HTTP/2 Server Push | TLS push frames | Browser receives bytes proactively | Largely deprecated — Chrome removed support in 2022 |
| 103 Early Hints | Informational response before the 200 | While the server is still rendering | Pages where TTFB is the bottleneck |
The 103's superpower is that it works during the server's "thinking time." Every other approach requires the server to be done. If your TTFB is fast (under ~100ms), 103 doesn't have much to add — there isn't enough idle time to fill. If your TTFB is slow, 103 is the only technique that helps.
Implementation Examples
Node.js / Express
Node added native support via response.writeEarlyHints():
import express from "express";
const app = express();
app.get("/", (req, res) => {
res.writeEarlyHints({
link: [
"</style.css>; rel=preload; as=style",
"</app.js>; rel=preload; as=script",
],
});
// Now do the slow work that justified sending hints early.
const data = await fetchSlowData();
res.render("home", { data });
});The writeEarlyHints() call flushes the 103 immediately. Anything you do after — even a multi-second async operation — happens while the browser is already preloading.
Fastify
Fastify exposes the same primitive:
fastify.get("/", async (request, reply) => {
reply.raw.writeEarlyHints({
link: "</style.css>; rel=preload; as=style",
});
const data = await fetchSlowData();
return reply.view("home.ejs", { data });
});NGINX (proxying upstream hints)
NGINX itself doesn't generate 103s, but it can pass them through from an upstream. As of NGINX 1.25+, you need to opt in:
http {
proxy_http_version 1.1;
http2_early_hints on; # forward 103s on HTTP/2 connections
server {
listen 443 ssl http2;
location / {
proxy_pass http://app_backend;
}
}
}If you're terminating HTTPS at NGINX and the upstream is sending 103s, this is the flag that lets them reach the browser.
Next.js (and the platform layer)
Next.js doesn't expose a per-route API for sending 103s yet. Instead, the platform you deploy to handles it: Vercel, for example, automatically generates 103 Early Hints for static assets it knows the page will need, with no app-code changes. Cloudflare and Fastly offer similar passthrough or generation features. For server-rendered Next.js pages on a custom Node server, you can call writeEarlyHints() from a custom server entry point.
Browser & CDN Support
Support has matured quickly:
- Chrome / Edge: Supported since Chrome 103 (June 2022)
- Firefox: Supported since Firefox 120 (November 2023)
- Safari: Supported since Safari 17 (September 2023)
- Cloudflare: Generates and forwards 103s
- Vercel: Generates 103s automatically for static assets per route
- Fastly: Forwards upstream 103s
In short: if you're targeting the modern web, you can rely on Early Hints landing where it matters. Older clients that don't understand 103 simply ignore it and wait for the 200, so it's safe to deploy without a fallback.
Common Pitfalls
-
Hinting resources you don't actually use. A bad hint isn't free — it consumes bandwidth and contends for connection slots. Only hint resources the page is certain to need. If a hint is conditional (e.g., depends on user login state), prefer to omit it.
-
Forgetting that the final response can be a redirect. If your handler ends up returning a 301 or 302, the resources you hinted at are now wasted — the browser is going somewhere else. Either suppress hints for routes that may redirect, or accept the cost as part of your latency budget.
-
Proxies and CDNs that don't forward 103. Some legacy load balancers and reverse proxies will buffer the response and drop the informational status. If you don't see 103s in DevTools after enabling them on your server, suspect the network path before you suspect your code. The fix is usually a configuration flag (like NGINX's
http2_early_hints on). -
HTTP/1.1 limitations. Early Hints work over HTTP/1.1 in theory, but in practice they're brittle on H1 because some intermediaries can't handle two response status lines on one connection. Real-world deployments standardize on HTTP/2 or HTTP/3 for any path where 103s matter.
-
Sending hints too late. The whole value proposition is filling idle time before the final response. If you call
writeEarlyHints()at the end of your handler — right before the final response — you're giving the browser zero head start. Send hints as the first thing your handler does.
When 103 Doesn't Help
- Fast TTFB pages. If your server responds in 30ms, there's no idle window to fill.
- Static HTML. Static files are served from disk or cache instantly — the browser can start parsing as soon as the bytes arrive. Use
<link rel="preload">in the HTML instead. - Pages where the critical resources change per request. If you can't predict what to hint, don't guess — wrong hints are worse than no hints.
- APIs and JSON endpoints. 103 is purely about pre-fetching resources for browser rendering. It has no role in machine-to-machine traffic.
Wrapping Up
103 Early Hints turns the server's "thinking time" into productive browser time. It's small, standardized, well-supported, and one of the few performance features you can adopt incrementally — slap it on your slowest server-rendered pages, watch LCP improve, and leave the rest of your code alone.
The rules of thumb:
- Use it on pages with slow TTFB; skip it on fast ones
- Only hint resources you're sure the page will use
- Send hints as the first thing in your handler, not the last
- Run on HTTP/2 or HTTP/3, not HTTP/1.1, for reliability
- If you don't see 103s in DevTools after deploying, check your CDN/proxy config
For more details, check out our pages on 103 Early Hints and 200 OK, or browse the rest of the 1xx informational codes. If your slow-TTFB pages also redirect frequently, our understanding 301 redirects post explains why hinting through a redirect is mostly wasted work.