SiteError.comYour friendly guide to HTTP status codes
Status CodesBlog
  1. Home
  2. Blog
  3. Understanding HTTP 103 Early Hints: Faster Pages with Smarter Preloads

Understanding HTTP 103 Early Hints: Faster Pages with Smarter Preloads

May 5, 20268 min read
1xxInformational

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:

  1. The server sends two response status lines on the same connection — the 103 first, then the final 200 (or whatever it ends up being).
  2. The Link headers in the 103 use the same syntax as Link: rel=preload headers 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:

TechniqueWhere it livesWhen the browser learns about itBest for
<link rel="preload">In the HTML <head>After HTML starts streamingResources discovered late in the document
Link: header on 200HTTP response headersWhen the 200 landsResources known statically per-route
HTTP/2 Server PushTLS push framesBrowser receives bytes proactivelyLargely deprecated — Chrome removed support in 2022
103 Early HintsInformational response before the 200While the server is still renderingPages 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

  1. 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.

  2. 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.

  3. 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).

  4. 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.

  5. 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.

Related Status Codes

👂100Continue🔄101Switching Protocols⏳102Processing💡103Early Hints
Back to Blog

Popular Status Codes

  • 200 OK
  • 301 Moved Permanently
  • 302 Found
  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found
  • 500 Internal Server Error
  • 502 Bad Gateway
  • 503 Service Unavailable

Compare Codes

  • 401 vs 403
  • 301 vs 302
  • 404 vs 410
  • 500 vs 502
  • Compare any codes →

Categories

  • Informational
  • Success
  • Redirection
  • Client Error
  • Server Error
  • NGINX
  • Cloudflare
  • AWS ELB
  • Microsoft IIS

Tools

  • Cheat Sheet
  • Status Code Quiz
  • URL Checker
  • API Playground
  • Blog

© 2026 SiteError.com. All rights reserved.