Skip to content

103 Early Hints

What is HTTP 103 Early Hints?

103 Early Hints
When you ask for a webpage, the computer has to build it which takes time. Inste...
HTTP 103 Early Hints status code illustration

Explain Like I’m 3

Before showing you the whole thing, someone gives you a hint about what’s coming! Like when someone says ‘Get ready, I’m bringing cookies!’ before they actually bring them. The hint helps you get ready faster!

Example: Mom is making dinner and says ‘Set the table, dinner is almost ready!’ You can start setting the table before dinner is actually done, so everything is faster when it’s ready.

Explain Like I’m 5

When you ask for a webpage, the computer has to build it which takes time. Instead of making you wait doing nothing, it says ‘103 Early Hints’ and tells your browser ‘You’re going to need these pictures and stylesheets!’ Your browser can start downloading those things early while waiting for the full page. It’s like getting a head start! This makes the webpage show up faster because some parts are already downloaded.

Example: You ask to see a webpage with lots of pictures. The server says ‘103 Early Hints - start downloading these 5 pictures and this font file!’ Your browser starts getting those while the server finishes making the page. When the page is ready, the pictures are already downloaded, so it shows up super fast!

Jr. Developer

HTTP 103 Early Hints allows servers to send preliminary HTTP headers before the final response, specifically Link headers that tell browsers to preload or preconnect to resources. While the server is processing the request (think database queries, API calls), it sends 103 with Link headers indicating critical resources (CSS, JS, fonts) the page will need. Browsers that support it start fetching those resources immediately, utilizing server think-time. This reduces metrics like First Contentful Paint (FCP) and Largest Contentful Paint (LCP). Defined in RFC 8297, it’s supported by modern browsers on HTTP/2+ connections. It’s particularly effective for pages with slow server-side processing but known static assets.

Example: Your server takes 800ms to generate HTML (database queries, rendering). Without 103, the browser waits 800ms idle, then discovers it needs app.css and logo.png, adding more latency. With 103, you send hints immediately: Link: </app.css>; rel=preload; as=style. The browser starts downloading CSS during the 800ms, so when HTML arrives, CSS is already loaded.

Code Example

// Express.js sending 103 Early Hints
app.get('/dashboard', async (req, res) => {
// Send early hints immediately
res.writeEarlyHints({
'link': [
'</css/dashboard.css>; rel=preload; as=style',
'</js/dashboard.js>; rel=preload; as=script',
'</fonts/roboto.woff2>; rel=preload; as=font',
'<https://cdn.example.com>; rel=preconnect'
]
});
// Now do slow processing
const data = await fetchDashboardData(); // 500ms
const html = renderTemplate('dashboard', data);
res.send(html);
});

Crash Course

103 Early Hints (RFC 8297) is an informational status code that enables servers to send resource hints via Link headers before the final response. The primary use case is preloading critical resources (stylesheets, scripts, fonts, images) and preconnecting to third-party origins during server processing time. The server sends 103 Early Hints with Link headers immediately upon receiving the request, then continues processing (database queries, API calls, rendering), and finally sends the actual 200 response with HTML. Browsers supporting Early Hints (Chrome 103+, Firefox 103+, Safari 16.4+) parse the Link headers and initiate resource fetches in parallel with waiting for the final response. This is most effective when server think-time is high (300ms+) and critical resources are known upfront. Early Hints only works reliably on HTTP/2 and HTTP/3 connections - HTTP/1.1 support is limited due to proxy issues with interim responses. Major CDNs (Cloudflare, Fastly, Akamai) and servers (NGINX 1.23+) support it.

Example: An e-commerce product page takes 600ms to generate (inventory check, price calculation, personalization). The server immediately sends 103 with hints for product.css, main.js, jquery.min.js, and preconnect to the CDN. During the 600ms, the browser downloads 3 JS files and establishes CDN connection. When the 200 response arrives with HTML, all critical resources are loaded, achieving 40% faster LCP.

Code Example

// Node.js http module with Early Hints
const http = require('http');
const server = http.createServer(async (req, res) => {
if (req.url === '/product') {
// Send 103 Early Hints immediately
res.writeEarlyHints({
link: [
'</static/product.css>; rel=preload; as=style',
'</static/main.js>; rel=preload; as=script',
'</static/jquery.min.js>; rel=preload; as=script',
'<https://cdn.example.com>; rel=preconnect',
'<https://analytics.example.com>; rel=preconnect'
]
});
// Perform slow operations
const product = await db.getProduct(req.query.id); // 200ms
const inventory = await checkInventory(product.id); // 150ms
const recommendations = await getRecommendations(product.id); // 250ms
// Generate and send final response
const html = renderProductPage(product, inventory, recommendations);
res.writeHead(200, {
'Content-Type': 'text/html',
'Link': '</static/product.css>; rel=stylesheet' // Final link headers
});
res.end(html);
}
});
server.listen(3000);

Deep Dive

HTTP status code 103 Early Hints, standardized in RFC 8297 (December 2017), addresses a fundamental inefficiency in web performance: browsers remaining idle during server think-time. Traditional request/response flow has the browser wait passively while the server processes (database queries, template rendering, API calls), then parse HTML to discover resource dependencies, creating a sequential waterfall. Early Hints inverts this by allowing servers to send resource hints proactively via interim 103 responses containing Link headers with rel=preload or rel=preconnect directives. Browsers compliant with the specification parse these hints and initiate resource fetches or connection establishments immediately, parallelizing work that would otherwise be sequential.

Technical Details

Protocol mechanics require HTTP/2 or HTTP/3 for reliable operation. HTTP/1.1’s lack of multiplexing and proxy behavior with interim responses make Early Hints unreliable on HTTP/1.1 - many proxies strip or mishandle 1xx responses. On HTTP/2+, the 103 response is sent as a HEADERS frame on the request stream before the final response HEADERS frame. Multiple 103 responses are permitted for a single request, allowing progressive hints as processing continues.

The Link header syntax follows RFC 8288 (Web Linking). Critical attributes: rel=preload (fetch resource), rel=preconnect (establish connection), rel=dns-prefetch (resolve DNS only), as=<type> (resource type: script, style, font, image, fetch), crossorigin (CORS mode for resources), type (MIME type hint). Example: Link: </app.css>; rel=preload; as=style; crossorigin.

Browser implementation varies. Chromium (Chrome/Edge 103+) supports preload and preconnect. Firefox 103+ supports both. Safari 16.4+ supports Early Hints. Older browsers ignore 103 responses gracefully (forward compatibility). Importantly, the final 200 response must include the same Link headers or actual resource references (link tags) - Early Hints are hints, not requirements.

Performance characteristics: Early Hints are most effective when server think-time exceeds round-trip time (RTT) to resources. For think-time of 500ms and RTT of 100ms, 3-5 resources can be fully fetched during processing. Diminishing returns occur with very short think-times (<100ms) where hint processing overhead outweighs benefits. Field data from Cloudflare shows 30% reduction in Time to First Byte perceived latency and 10-15% improvement in LCP for suitable pages.

Security and privacy: Early Hints can leak information about user state if hints vary based on authentication. Example: authenticated users get hints for admin.css, revealing login status before final response. Mitigation: send consistent hints regardless of auth state, or ensure hints don’t reveal sensitive information. Cross-origin resource timing applies - preloaded cross-origin resources follow CORS and timing restrictions.

Caching implications: 103 responses themselves are not cacheable (interim responses never are). However, resources preloaded via Early Hints follow normal HTTP caching rules. CDN edge servers can generate 103 responses based on static configuration even if origin doesn’t support it, providing benefits without origin changes.

Operational considerations: Early Hints increase request processing complexity. Servers must identify critical resources upfront, before template rendering completes. This requires framework integration or build-time asset manifest generation. Over-hinting (too many preloads) wastes bandwidth and can delay final response if connection is saturated. Under-hinting misses optimization opportunities. Optimal hint count is typically 3-8 critical resources.

Code Example

// Production-grade Early Hints with asset manifest
const express = require('express');
const fs = require('fs');
const app = express();
// Load build-time asset manifest
const assetManifest = JSON.parse(
fs.readFileSync('./dist/asset-manifest.json')
);
// Generate Early Hints from manifest
function getEarlyHintsForRoute(route) {
const hints = [];
// Critical CSS for route
if (assetManifest.routes[route]?.criticalCSS) {
hints.push(
`<${assetManifest.routes[route].criticalCSS}>; rel=preload; as=style`
);
}
// Critical JS bundles
const jsChunks = assetManifest.routes[route]?.criticalJS || [];
jsChunks.forEach(chunk => {
hints.push(`<${chunk}>; rel=preload; as=script; crossorigin`);
});
// Critical fonts
const fonts = assetManifest.criticalFonts || [];
fonts.forEach(font => {
hints.push(
`<${font}>; rel=preload; as=font; type=font/woff2; crossorigin`
);
});
// Third-party origins
const origins = assetManifest.preconnectOrigins || [];
origins.forEach(origin => {
hints.push(`<${origin}>; rel=preconnect; crossorigin`);
});
return hints;
}
app.get('/product/:id', async (req, res) => {
// Send Early Hints immediately - before any processing
const hints = getEarlyHintsForRoute('product');
if (hints.length > 0 && req.httpVersion >= '2.0') {
res.writeEarlyHints({ link: hints });
}
// Now perform slow operations
const startTime = Date.now();
try {
const [product, inventory, reviews, recommendations] = await Promise.all([
db.getProduct(req.params.id),
checkInventory(req.params.id),
getReviews(req.params.id),
getRecommendations(req.params.id)
]);
const thinkTime = Date.now() - startTime;
// Generate final response
const html = renderTemplate('product', {
product,
inventory,
reviews,
recommendations
});
// Include same resources in final response
const linkHeaders = hints.map(h => h.replace('; rel=preload', '; rel=preload'));
res.set({
'Content-Type': 'text/html',
'Link': linkHeaders.join(', '),
'Server-Timing': `think;dur=${thinkTime}`
});
res.send(html);
} catch (error) {
console.error('Error:', error);
res.status(500).send('Internal Server Error');
}
});
app.listen(3000);

Frequently Asked Questions

Which browsers support 103 Early Hints?

Chrome/Edge 103+, Firefox 103+, and Safari 16.4+ support Early Hints. However, support only works reliably on HTTP/2 or HTTP/3 connections - HTTP/1.1 support is limited. Older browsers gracefully ignore 103 responses without breaking. Note that Googlebot does NOT support Early Hints and will ignore them.

What's the difference between Early Hints and HTTP/2 Server Push?

Server Push proactively sends resources to the client without hints, risking wasted bandwidth if the resource is already cached. Early Hints sends hints that let the browser decide whether to fetch based on its cache state. Early Hints is more efficient and has replaced Server Push (which was removed from Chrome). Early Hints provides suggestions; Server Push forces resources.

When should I use rel=preload vs rel=preconnect in Early Hints?

Use rel=preload for specific resources you know the page needs (CSS, JS, fonts, critical images). Use rel=preconnect for third-party origins where you know connections will be needed but not specific resources (CDNs, analytics, API servers). Preload fetches the actual resource; preconnect only establishes the connection (DNS, TCP, TLS).

Can Early Hints actually hurt performance?

Yes, if misused. Over-hinting (too many preloads) can saturate the connection and delay the final response. Hinting for resources that won't be used wastes bandwidth. Hinting on fast pages (sub-100ms think-time) adds overhead without benefit. Optimal is 3-8 critical resources on pages with 300ms+ server processing time.

Do I need to change my origin server to use Early Hints with a CDN?

Not necessarily. Many CDNs (Cloudflare, Fastly, Akamai) can generate 103 responses at the edge based on configuration, even if your origin doesn't support it. This provides the performance benefit without requiring origin changes. However, origin-generated hints can be more dynamic and accurate.

Common Causes

  • Server with high think-time (database queries, API calls) sending resource hints
  • CDN edge server generating Early Hints based on static configuration
  • Node.js server using res.writeEarlyHints() for critical resources
  • Framework with built-in Early Hints support (Next.js, Nuxt, etc.)
  • Page with known critical resources (CSS, JS, fonts) that benefit from preloading
  • Third-party resource optimization sending preconnect hints

Implementation Guidance

  • Server-side: Use res.writeEarlyHints() in Node.js or equivalent in your framework
  • Identify critical resources using Lighthouse or WebPageTest ‘critical request chains’
  • Send Early Hints only on HTTP/2 or HTTP/3 connections (check req.httpVersion)
  • Include Link headers in both 103 response and final response for consistency
  • Limit hints to 3-8 critical resources to avoid connection saturation
  • Use rel=preload for specific resources, rel=preconnect for third-party origins
  • Generate asset manifest at build time listing critical resources per route
  • Test with Chrome DevTools Network tab - look for Early Hints in timing waterfall
  • Monitor performance impact with Real User Monitoring (RUM) - track LCP improvements
  • Consider CDN-level Early Hints if origin changes are difficult
📚 Sources:
🕐 Last updated: January 5, 2026

Comments