On this page
HTTP 103 Early Hints: Preloading Resources During Server Think-Time
How HTTP 103 Early Hints (RFC 8297) works in practice: browser behavior, measurable LCP impact, how to configure it in Nginx and via CDNs, CSP interaction, and what to check when it isn't working.
Reference Card
- RFC
- 8297 — An HTTP Status Code for Indicating Hints
- Status
- Experimental RFC, December 2017
- Protocol requirement
- HTTP/2 or HTTP/3 strongly recommended. HTTP/1.1 intermediaries frequently mishandle 1xx responses
- Supported hint types
- rel=preload, rel=preconnect. Not prefetch or dns-prefetch
- Browser support
- Chrome 103+ (2022), Firefox 120+ (2023), Safari 17+ (2023), Edge 103+
- Primary CWV impact
- LCP, FCP. Does not reduce TTFB
- NGINX native support
- ≥ 1.29.0 (proxy passthrough of upstream 103)
- Last verified
- August 2026 — verified against RFC 8297, MDN compatibility data, NGINX 1.29.0 changelog
Key takeaways
- ✓ Does not reduce TTFB; it uses existing think-time more efficiently
- ✓ Improves LCP and FCP by starting critical fetches before HTML arrives
- ✓ Requires HTTP/2 or HTTP/3 in practice
- ✓ Only hint 2-4 resources; over-hinting causes mobile regression
- ✓ The
as,crossorigin, andtypeattributes must exactly match the HTML reference - ✓ Every font preload hint must include
crossorigin
At a glance
| Question | Answer |
|---|---|
| Improves TTFB? | ✗ No |
| Improves LCP? | ✓ Yes |
| Improves FCP? | ✓ Usually |
| Improves CLS? | ⚠ Indirectly |
| HTTP/1.1? | ⚠ Unreliable |
| Recommended hints | 2-4 |
Short answer (RFC 8297):
103 Early Hintsis an HTTP informational response that lets a server sendLink: rel=preloadandLink: rel=preconnectheaders to the browser before the final response is ready. The browser can begin fetching critical resources during server processing time. This does not reduce TTFB but converts idle server think-time into productive parallel resource fetching, improving LCP and FCP without changing the server’s rendering pipeline.
Who this is for: backend developers configuring server response pipelines, performance developers measuring Core Web Vitals, DevOps developers managing CDN and proxy layers, and framework authors evaluating where to expose 103 support.
Methodology: This guide cross-checks RFC 8297, MDN Web Docs, the HTML Living Standard preload scanner specification, the Fetch specification CORS protocol, the NGINX 1.29.0 changelog, and published CDN documentation. Framework-specific claims are marked with their verification status.
Written and reviewed by: The Web Specification Studio team. Reviewed against: RFC 8297, MDN, WHATWG HTML, Fetch, NGINX, Cloudflare. Last tested: Verified against current MDN compatibility data (August 2026).
1. Browser Support
Per MDN compatibility data, verified August 2026:
| Browser | Supports 103 | Since | Notes |
|---|---|---|---|
| Chrome | 103 (July 2022) | preload and preconnect only | |
| Edge | 103 (July 2022) | Same engine as Chrome | |
| Firefox | 120 (November 2023) | preload and preconnect only | |
| Safari | Partial | 17+ (September 2023) | preconnect confirmed. Safari support has historically lagged Chromium and Firefox; preload support remains unreliable or absent in Safari as of the last verification. Verify current compatibility on MDN before relying on rel=preload in production. Mitigation: always include a rel=preconnect hint for any third-party origin whose assets you would otherwise hint with preload |
| HTTP/1.1 clients | — | 1xx responses unreliably handled by intermediaries | |
rel=prefetch in 103 | — | Not implemented in any major browser | |
rel=modulepreload in 103 | — | Inconsistent across browsers; do not rely on it |
2. How It Works: The Request Timeline
Without Early Hints, a browser makes a request and waits in silence while the server processes it. No preloading can begin until the first byte of HTML arrives. As defined in RFC 8297, Section 2, the 103 status allows the server to transmit Link headers as an informational response before the final 200 OK. (Note: The RFC permits multiple 103 responses, though browsers typically only process the first.)
Full exchange showing the 103 preceding the 200:
GET /dashboard HTTP/2
Host: example.com
HTTP/2 103 Early Hints
Link: </fonts/inter.woff2>; rel=preload; as=font; type="font/woff2"; crossorigin
Link: </css/critical.css>; rel=preload; as=style
Link: <https://api.example.com>; rel=preconnect
[... server processes request: DB query, template render ...]
HTTP/2 200 OK
Content-Type: text/html; charset=utf-8
Link: </fonts/inter.woff2>; rel=preload; as=font; type="font/woff2"; crossorigin
<!doctype html>
<html>...
When the browser receives the 103 and the hinted relationship type is one it implements (such as preload or preconnect), it can begin the associated work immediately, overlapping resource discovery with server processing. Those fetches run in parallel with the server’s remaining work: database queries, template rendering, response serialization.
3. The HTTP/2 Requirement
RFC 8297 does not formally restrict 103 to HTTP/2, but production deployments and browser implementations generally rely on HTTP/2 or HTTP/3 because HTTP/1.1 intermediaries frequently mishandle informational responses. HTTP/1.1 technically permits 1xx responses, but many load balancers, proxies, and CDN edge nodes strip or discard them before they reach the browser.
HTTP/2 multiplexing allows the server to send the 103 response on the same stream as the eventual 200, and the browser processes them sequentially and correctly without tearing down and re-establishing the connection.
Verify your connection negotiates HTTP/2 before relying on Early Hints in production:
# Check the negotiated protocol
curl -sI --http2 https://example.com/ | head -1
# Expected: HTTP/2 200
# Check that 103 is being emitted
curl -v --http2 https://example.com/ 2>&1 | grep -E "HTTP/2 (103|200)"
# Expected: HTTP/2 103 then HTTP/2 200
If HTTP/1.1 appears in the output, Early Hints will not reliably reach the browser. Common causes are covered in the Troubleshooting section.
4. What the Browser Does with a 103
The preload scanner and why 103 matters
As specified in the HTML Living Standard, the browser’s preload scanner is a secondary speculative parser that scans the incoming HTML byte stream for resource references (<script src>, <link href>, <img src>) while the main parser builds the DOM. It is one of the most impactful browser optimizations for page load performance and runs concurrently with the main parser.
The preload scanner cannot run until the browser has received the first bytes of the HTML response. Early Hints effectively moves part of the preload scanner’s discovery work before the HTML arrives; the browser acts on Link headers in the 103 response using the same fetch and priority infrastructure as the preload scanner.
Chrome’s fetch priority scheduler
A simplified conceptual view of Chromium’s scheduling behavior:
Modern Chromium-based browsers use a fetch priority scheduler that assigns queue priority based on resource type and the fetchpriority attribute. Per the Priority Hints specification, resources preloaded via Early Hints follow the same priority rules as equivalent HTML <link rel="preload"> declarations. The as attribute determines the default priority:
| Priority | Resource types |
|---|---|
| Highest | CSS (as=style) |
| High | Fonts (as=font), scripts in <head> (as=script) |
| Medium | Images above the fold with fetchpriority=high |
| Low | Images below the fold, prefetch |
Note: The fetchpriority attribute cannot be set on the Link header itself. It must be set on the HTML <link>, <img>, or <script> tag. The browser will match the Early Hint fetch to the HTML element and upgrade its priority accordingly.
For LCP images, pair the 103 preload hint with fetchpriority="high" on the <img> element in the HTML to ensure the browser’s priority scheduler treats the image as critical for rendering:
<img src="/hero.avif" fetchpriority="high" alt="Hero image">
Which hint types the browser honours
| Hint type | In 103 | Behaviour |
|---|---|---|
rel=preload | ✓ | Fetches the hinted resource at the priority implied by as |
rel=preconnect | ✓ | Initiates DNS + TCP + TLS handshake to the specified origin |
rel=prefetch | ✗ | Not implemented in 103 in any major browser |
rel=dns-prefetch | ✗ | Not supported in 103 responses |
rel=modulepreload | ⚠ | Inconsistent across browsers |
The as attribute is mandatory
The as attribute tells the browser the resource type and determines fetch priority, CORS mode, and cache partition. Per the Fetch specification, a preload hint missing as falls back to a low-priority generic fetch with no CORS credentials. If the HTML later references the same resource with a specific type, the browser may not match it to the in-flight fetch and issues a second request.
| Resource type | as value | CORS required |
|---|---|---|
| CSS stylesheet | as=style | No (same-origin default) |
| Classic or module script | as=script | Only if cross-origin |
| Web font | as=font | Always: fonts use CORS regardless of origin |
| LCP image | as=image | Only if cross-origin |
| JSON / API response | as=fetch | Yes, always |
5. Core Web Vitals Impact
Early Hints directly improves two Core Web Vitals metrics, has no effect on two, and may have a marginal indirect effect on one.
Largest Contentful Paint (LCP)
LCP measures when the largest visible element finishes rendering. For most pages this is a hero image or a heading rendered in a web font. If either is discovered late (after HTML parsing finds the <img> or @font-face declaration), LCP is delayed by the full download time.
Early Hints moves that discovery earlier, overlapping the fetch with server think-time. The gain is proportional to server think-time: minimal on a static server with 20ms TTFB, significant on a server-rendered application with 200–400ms of processing time.
For LCP images, combine the hint with fetchpriority="high" on the <img> tag:
# 103 hint
Link: </hero.avif>; rel=preload; as=image
# HTML
<img src="/hero.avif" fetchpriority="high" alt="...">
First Contentful Paint (FCP)
FCP measures when the first content appears. Critical render-blocking CSS is the most common cause of delayed FCP. A 103 preload hint for critical CSS may cause it to arrive before or simultaneously with the HTML, allowing the browser to begin rendering sooner.
TTFB: No impact
Early Hints does not reduce TTFB. TTFB is measured from request start to the first byte of the final HTTP response; the 103 informational response does not count. The server still takes the same time to process the request.
A common misconception is that Early Hints makes the server faster. It does not. It makes the browser’s waiting time more productive.
CLS: No direct impact
Cumulative Layout Shift is caused by late-loading content displacing already-rendered elements. Early Hints can indirectly reduce CLS if it prevents a web font from arriving after text has already rendered with a fallback font, but this effect is not guaranteed and depends on the font-display strategy.
INP: No impact
Interaction to Next Paint measures JavaScript event loop responsiveness. Early Hints has no effect on script execution or interaction latency.
6. 103 Early Hints vs Link Header in the 200 Response
A Link: rel=preload header can be sent in either the 103 response or the 200 response. These are different in one critical way: timing.
| Technique | When browser sees it | Overlap with think-time |
|---|---|---|
Link header in 103 | Before server finishes | ✓ Yes (entire think-time) |
Link header in 200 | Same moment as HTML | ✗ No overlap |
<link rel="preload"> in <head> | After preload scanner runs | ✗ No overlap |
A Link header in the 200 response is functionally equivalent to a <link rel="preload"> in <head> from a timing perspective: both are discovered only after the HTML byte stream begins arriving. The 103 is the only mechanism that allows preloading to begin before the server has finished generating the response.
This distinction matters most on server-rendered applications with meaningful TTFB. On static files served directly from a CDN with sub-10ms TTFB, the difference between 103 and a 200 Link header is negligible.
7. Should You Use Early Hints?
Use Early Hints when:
- Your server think-time (TTFB) exceeds ~100ms under normal load
- The page has critical render-blocking resources (CSS, above-the-fold fonts, LCP image)
- The connection stack supports HTTP/2 or HTTP/3 end-to-end
- Intermediaries preserve 1xx responses
Skip Early Hints when:
- Server TTFB is under 50ms. The overlap window is too small to matter
- All assets are already cached. When the browser checks whether another fetch is necessary for a hinted resource it has in cache, it may skip the fetch. The exact behavior depends on the resource type, cache mode, and browser implementation. High repeat-visitor traffic reduces the benefit
- There are no render-blocking resources. Nothing to hint that would affect rendering metrics
- HTTP/1.1 is the dominant protocol for your traffic
- An intermediary (WAF, load balancer, legacy proxy) strips 1xx responses before they reach the browser
- You cannot limit to 2-4 hints. Over-hinting on mobile causes resource contention
8. Decision Tree
9. Implementation Comparison
| Environment | Native 103 support | Notes |
|---|---|---|
Node.js node:http2 (raw) | ✓ | stream.additionalHeaders({ ':status': 103, ... }) |
| NGINX ≥ 1.29.0 | ✓ (proxy passthrough only) | Passes through 103 responses from upstream backends using the early_hints directive. NGINX’s native Early Hints support focuses on forwarding upstream informational responses rather than acting as a standalone hint-generation system. NGINX 1.29.0 changelog |
| NGINX < 1.29.0 | ✗ | No Early Hints support at the proxy layer. Emit 103 directly from the upstream application |
| CDN edge workers | ✓ | Set Link header on response; CDN emits 103 on subsequent requests after cache warm |
| Express.js | ⚠ Partial | Requires direct access to the underlying HTTP/2 stream; Express abstracts over it |
| Next.js | ⚠ No stable API | No stable, documented framework-level API for emitting 103 as of publication. Implement at the reverse proxy or CDN layer |
| Apache httpd | ✗ | No native Early Hints module documented as of publication. Implement at the CDN or reverse proxy layer |
| Caddy | ⚠ Check docs | Support varies by version. Verify against current Caddy documentation before relying on it |
| PHP-FPM stacks | ✗ | In PHP-FPM deployments, the FastCGI protocol and web server buffering between the PHP process and the client connection prevent practical emission of a 103 before the full response body. Implement at NGINX or the CDN layer |
| WordPress | ✗ | No core support. Implement at the reverse proxy or CDN layer |
10. How to Implement
Node.js node:http2 (raw HTTP/2 server)
The Node.js built-in node:http2 module exposes stream.additionalHeaders(), which sends an informational response on the active stream before the final response:
import http2 from 'node:http2';
import fs from 'node:fs';
const server = http2.createSecureServer({
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.crt'),
});
server.on('stream', async (stream, headers) => {
// Emit 103 immediately — browser begins preloading in parallel
stream.additionalHeaders({
':status': 103,
link: [
'</css/critical.css>; rel=preload; as=style',
'</fonts/inter.woff2>; rel=preload; as=font; type="font/woff2"; crossorigin',
].join(', '),
});
// Server does its work while browser fetches hinted resources
const html = await renderPage(headers[':path']);
stream.respond({
':status': 200,
'content-type': 'text/html; charset=utf-8',
});
stream.end(html);
});
server.listen(8443);
NGINX 1.29.0+ (upstream proxy passthrough)
Per the NGINX 1.29.0 changelog, the early_hints directive passes through 103 responses that an upstream backend emits. NGINX’s native Early Hints support focuses on forwarding upstream informational responses rather than acting as a standalone hint-generation system. Static hint generation at the NGINX layer is not part of the 1.29.0 release; implement that at the CDN edge instead.
Correct configuration using a map to enable passthrough only for navigation requests over HTTP/2 or HTTP/3:
# Pass through upstream 103 responses only on HTTP/2+ navigation requests
map $http_sec_fetch_mode $enable_early_hints {
navigate $http2$http3; # non-empty string enables passthrough
default ""; # empty string disables it
}
server {
listen 443 ssl;
http2 on;
location / {
early_hints $enable_early_hints;
proxy_pass http://upstream_app; # upstream must emit the 103
}
}
The upstream application is responsible for emitting the 103 with Link headers before the 200. The Node.js node:http2 pattern above works directly. For Go, use http.ResponseController with EnableFullDuplex and write the informational response before the final one:
// Go 1.21+ — net/http informational response
// Note: As of Go 1.21, there is no direct Write103 API in the standard library.
// Check current net/http documentation or framework-specific experimental helpers.
func handler(w http.ResponseWriter, r *http.Request) {
// Illustrative: framework-specific early hints implementation
if helper, ok := w.(EarlyHintsWriter); ok {
helper.WriteEarlyHints(
"</css/critical.css>; rel=preload; as=style, " +
"</fonts/inter.woff2>; rel=preload; as=font; type=\"font/woff2\"; crossorigin",
)
}
// Do actual work then respond normally
html := renderPage(r.URL.Path)
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprint(w, html)
}
Next.js
Next.js does not currently expose a stable, documented framework-level API for emitting HTTP 103 responses. Undocumented or unstable_* APIs in Next.js are subject to breaking changes without notice and should not be used in production for this purpose. In Next.js deployments, configure Early Hints at the NGINX reverse proxy or CDN layer, where you have direct control over the response pipeline.
CDN edge worker (generic pattern)
CDN edge workers allow programmatic control of response headers before the origin is even contacted. The CDN reads the Link headers from your 200 response and emits them as a 103 on subsequent requests for the same URL:
// Generic edge worker pattern — syntax varies by CDN platform
addEventListener('fetch', (event) => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
const response = await fetch(request);
const headers = new Headers(response.headers);
headers.set('Link', [
'</css/critical.css>; rel=preload; as=style',
'</fonts/inter.woff2>; rel=preload; as=font; type="font/woff2"; crossorigin',
].join(', '));
return new Response(response.body, {
status: response.status,
headers,
});
}
Note: CDN automatic Early Hints mode caches Link headers from the origin 200 response and emits them as 103 on subsequent requests. The very first request to a cold CDN node will not receive a 103 because the CDN has not yet seen the origin’s headers for that URL. To guarantee Early Hints on cold requests, emit the 103 programmatically in the edge worker before the origin fetch.
11. CDN Support
Major CDN platforms implement Early Hints in two distinct modes: automatic (passive) and programmatic (active). Understanding which mode your CDN uses determines how hints behave on cold requests.
Automatic mode works by observing Link headers on origin 200 responses, caching them at the edge, and emitting them as 103 on subsequent requests for the same URL. The first request to a cold edge node will not receive a 103 because the CDN has not yet seen the origin’s headers. Once the edge has cached the Link headers from a 200, all subsequent requests to that URL receive the 103 before the origin is contacted.
Programmatic mode, via edge worker scripts, allows emitting a 103 before the origin fetch even completes. This guarantees Early Hints on every request including cold ones, but requires writing and deploying an edge worker.
Platform specifics (verify against current vendor documentation, as CDN features evolve):
- Cloudflare automatic mode: Enabled in Speed → Optimization → Content Optimization → Early Hints. Available on all plan tiers. Caches
Linkheaders from the origin200. First cold-edge request receives no103. Cloudflare Early Hints documentation. - Cloudflare Workers (programmatic): Write a Worker that sets
Linkheaders and returns the response. Cloudflare emits the103before forwarding to origin on that same request. Guarantees Early Hints on every request. - Fastly Compute (programmatic): Fastly Compute provides APIs for emitting informational responses. Check the current platform documentation for language-specific implementations. This is a fully programmatic approach with no cold-request gap.
- Akamai EdgeWorkers: Check current Akamai EdgeWorkers documentation for informational response support. As of publication, support varies by product tier.
CDN feature availability changes frequently. Treat the above as a starting point and verify against current documentation.
12. The Double-Fetch Trap
A misconfigured Early Hints setup causes the browser to fetch the same resource twice: once from the 103 hint and again from the HTML reference. Per the Fetch specification, the browser’s preload cache is keyed on the resource URL and the fetch credentials mode. A mismatch between the hint’s attributes and the HTML reference’s attributes causes the cache lookup to fail.
The most common mismatch is fonts:
# 103 hint — missing crossorigin (wrong)
Link: </fonts/inter.woff2>; rel=preload; as=font; type="font/woff2"
# HTML reference
<link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin>
The 103 hint fetches without CORS credentials. The HTML <link> requests with CORS credentials (anonymous mode). These are separate cache entries. The browser issues two requests; the first is wasted because fonts require CORS and the non-CORS response is unusable.
The correct 103 hint for any font is always:
Link: </fonts/inter.woff2>; rel=preload; as=font; type="font/woff2"; crossorigin
crossorigin with no value is equivalent to crossorigin="anonymous", the default CORS mode used by @font-face fetches.
Every attribute on the 103 Link header must exactly match the corresponding element attribute in the HTML: as, crossorigin, type, and media must all be identical.
Note that the fetchpriority attribute is not part of the Link header syntax and cannot be set on a 103 hint. The fetchpriority attribute only applies to HTML elements (<img>, <link>, <script>). To signal high priority for an Early Hints preloaded resource, the <img fetchpriority="high"> or <link rel="preload" fetchpriority="high"> in the HTML document is the correct mechanism. The preload cache will match the in-flight fetch from the 103 hint against that element when it is encountered.
13. Early Hints vs Related Techniques
| Technique | Starts before HTML | Browser controls priority | Duplicate-safe | When to use |
|---|---|---|---|---|
| 103 Early Hints | ✓ | ✓ | ✓ | Server think-time > 100ms + critical render-blocking resources |
<link rel="preload"> in HTML | ✗ | ✓ | ✓ | Critical resources in <head>; page-specific assets |
Link: rel=preload in 200 header | ✗ | ✓ | ✓ | Same timing as HTML preload; useful when HTML is not directly available |
<link rel="preconnect"> | ✗ | ✓ | N/A | Warm third-party origins before they appear in HTML |
<link rel="dns-prefetch"> | ✗ | N/A | N/A | Low-cost origin resolution; fallback when preconnect is too aggressive |
| HTTP/2 Server Push | ✓ | ✗ | ✗ | Effectively obsolete; Chrome removed support in 106 (2022) |
Early Hints is the recommended replacement for HTTP/2 Server Push. Server Push transferred data the browser may already have had cached, with no mechanism for the browser to signal what it did not need. Early Hints communicates intent only; the browser checks its own cache and decides whether to fetch. For a broader view of the resource loading hint ecosystem (preload, preconnect, prefetch, dns-prefetch), see the companion Resource Hints article.
For client-side speculative navigation, prerendering full pages before the user clicks, see the Speculation Rules API. Early Hints and Speculation Rules solve adjacent but distinct problems: Early Hints accelerates the current page’s critical resources during server processing; Speculation Rules prefetches or prerenders future pages in the background.
14. Performance Budget: What to Hint
RFC 8297 does not specify a maximum number of hints. However, multiple concurrent fetches initiated during the document’s connection establishment phase can compete for bandwidth on constrained mobile connections. Limit hints to resources definitively on the critical rendering path:
| Slot | Resource | Hint type |
|---|---|---|
| 1 | Critical render-blocking CSS | rel=preload; as=style |
| 2 | LCP web font | rel=preload; as=font; crossorigin |
| 3 | LCP image (if not font-based) | rel=preload; as=image |
| 4 | Required third-party origin | rel=preconnect |
Stop at four. Below-the-fold images, lazy-loaded JavaScript bundles, analytics scripts, chat widgets, and A/B testing payloads must never appear in Early Hints; they add network pressure without advancing the critical rendering path.
15. Browser Internals: The Preload Scanner
Understanding why Early Hints works requires understanding the browser’s preload scanner.
As specified in the HTML Living Standard, when the browser receives HTML bytes it runs two parsers simultaneously:
- Main parser: Builds the DOM tree, applies stylesheets, executes synchronous scripts. Can be blocked by
<script>tags that do not haveasyncordefer. - Preload scanner (speculative parser): A lightweight secondary scanner that reads ahead in the byte stream looking for
src,href,srcset, and similar attributes. Issues fetch requests without waiting for the main parser to unblock.
The preload scanner is highly effective for resources in <head> that appear in the initial HTML payload. Its fundamental constraint: it cannot run until the first bytes of HTML arrive. On a server with 400ms of processing time, the preload scanner and the main parser both sit idle for those 400ms.
Early Hints gives the browser a preload-scanner-equivalent that runs before the HTML exists. The browser processes Link headers in the 103 response using the same fetch and priority scheduler infrastructure as preload scanner discoveries; Chromium’s network stack routes them through the same fetch priority queue. The practical effect is that the preload pipeline is advanced by exactly the duration of server think-time.
16. Lighthouse, Tooling, and RUM
Lighthouse does not have a dedicated Early Hints audit. It measures simulated load performance but does not detect whether a 103 was received or whether it prevented resource discovery latency. Lighthouse is not the right tool to validate Early Hints.
curl is the primary protocol verification tool. Test with curl through your actual production path, not just directly against the origin, to verify intermediary behavior:
curl -v --http2 https://example.com/ 2>&1 | grep -E "HTTP/2 (103|200)|^< [Ll]ink:"
# Look for HTTP/2 103 appearing before HTTP/2 200
In Chrome DevTools, open the Network tab, hard-reload (Shift+F5), and click the document request. In the Timing panel, font, CSS, or image fetches that begin during the document’s Waiting (TTFB) window are Early Hints fetches. Chrome DevTools may not display 103 responses as separate entries in all versions; the evidence is indirect.
WebPageTest’s waterfall view shows the overlap between document TTFB and asset fetch timelines explicitly. A font or CSS bar beginning during the document’s Waiting phase is visual confirmation. WebPageTest labels Early Hints connections in some view modes.
For real-user monitoring (RUM), segment LCP by URL group before and after enabling Early Hints, using a 7-day minimum window to account for traffic variation and cache warm-up. Do not rely on aggregate LCP averages alone; the gain is most visible at the 75th and 95th percentiles where TTFB variance is highest. Clean attribution requires isolating the Early Hints rollout from other concurrent changes (deployments, CDN config, image format changes) and verifying that the traffic mix (mobile vs desktop, new vs returning visitors) is stable across the measurement period. Returning visitors with warm caches will show smaller gains than new visitors, which can mask the improvement in blended metrics.
Performance gains depend primarily on server think-time, resource selection, browser support, and network conditions. There is no universal improvement percentage.
17. Security and CSP Interaction
Per RFC 8297, Section 2, a server may include any response headers in a 103 response. Browsers enforce Content-Security-Policy headers found in the 103 response for resources fetched as a result of those hints.
This has two practical consequences. First, if your CSP restricts font or style origins, those restrictions apply to Early Hints fetches. A hint for a font from https://fonts.gstatic.com will be blocked if the font-src directive does not permit that origin, even if the 103 arrives before the HTML. Second, you can include the CSP in the 103 response so that it is enforced for early resources without waiting for the 200. This avoids a window where Early Hints fetches occur before the browser has seen the policy. Browsers generally ignore headers they don’t implement in 103 responses.
For sites in a Content-Security-Policy-Report-Only rollout, the report-only header in the 103 will generate violation reports for any Early Hints fetches that would have been blocked. This is useful for auditing but produces noise if Early Hints fires at scale before the policy is finalized.
18. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
No HTTP/2 103 in curl output | Server not emitting 103, or CDN cache not yet warmed | Check server/CDN configuration; warm the cache |
HTTP/1.1 200 in curl output | Connection negotiated HTTP/1.1 | Enable HTTP/2 (h2) on server or CDN; verify ALPN |
| 103 sent but browser does not act | Intermediary (proxy, WAF, load balancer) stripping 1xx | Audit proxy config; some proxies have proxy_pass_header or 1xx passthrough settings |
| Font fetched twice | crossorigin missing or mismatched between hint and HTML | Add crossorigin to the 103 hint; verify it matches the HTML <link> |
| CSS or image fetched twice | as attribute missing or mismatched | Verify as value matches resource type in both hint and HTML |
| 103 not firing on first request | CDN automatic mode requires a prior 200 to cache Link headers | Warm cache with a request, or emit 103 programmatically in edge worker |
| ALPN falling back to HTTP/1.1 | Server not advertising h2 in TLS ALPN extension | Configure server to advertise h2; check TLS config |
| Hints emitted but no LCP improvement | Server think-time too short, or hinted resource not on critical path | Profile TTFB; only hint resources that block the first render |
| Mobile LCP regressed after enabling | Over-hinting causing bandwidth contention | Reduce to 1–2 hints; remove below-the-fold and non-critical resources |
rel=modulepreload not working in 103 | Inconsistent browser support | Use rel=preload; as=script instead |
rel=preload hint ignored in Safari | Safari support has historically lagged Chromium and Firefox; verify current compatibility | Use rel=preconnect for third-party origins; for same-origin fonts and CSS, include <link rel="preload"> in the HTML <head> as a fallback |
| Intermediary (Varnish, Squid, IIS ARR, AWS ALB) stripping 1xx | Many common intermediaries discard informational responses by default | Varnish: VCL pass with beresp.was_304 handling; Squid: configure ignore-expect-100; AWS ALB: has historically not forwarded 1xx responses from targets (verify current behavior); IIS ARR: does not pass through 1xx |
17. FAQ
Does 103 Early Hints reduce TTFB?
No. Per RFC 8297, the 103 response is an informational (1xx) response, not the final response. TTFB is measured from request start to the first byte of the 200 OK. The server’s processing time is unchanged. Early Hints makes the browser’s waiting time productive but does not accelerate the server.
Is Early Hints worth it?
It depends on two variables: server think-time and critical resource count. If your server-rendered pages have TTFB above 100ms and the page has render-blocking CSS or above-the-fold fonts, Early Hints produces measurable LCP and FCP improvements in real-user monitoring data. For static files served from a CDN with sub-10ms TTFB, the benefit is negligible.
Can Early Hints preload images?
Yes, with rel=preload; as=image. This is most useful for the LCP image. Since the 103 is emitted before the browser knows the viewport dimensions, ensure the hinted resource matches the image most users will actually load. Pair with fetchpriority="high" on the <img> tag in the HTML so the browser’s priority scheduler treats it as highest priority.
Does Cloudflare automatically enable Early Hints?
Cloudflare has an automatic Early Hints feature that caches Link headers from origin 200 responses and emits them as 103 on subsequent requests. It is available on all plan tiers and configured in Speed > Optimization > Content Optimization > Early Hints. The first request to a cold edge node will not receive a 103; Cloudflare needs to have seen the origin’s Link headers at least once. See Cloudflare’s Early Hints documentation for current configuration details.
Does HTTP/3 improve Early Hints?
HTTP/3 uses QUIC, which eliminates TCP head-of-line blocking. Multiple parallel fetches initiated by Early Hints on an HTTP/3 connection suffer less from packet loss on lossy networks because each stream is independent. The 103 mechanism itself is identical between HTTP/2 and HTTP/3. HTTP/3’s benefit is in the reliability of those parallel fetches, not the Early Hints mechanism.
Can Early Hints replace <link rel="preload"> in HTML?
No. They serve different discovery moments. <link rel="preload"> in HTML is discovered by the preload scanner after HTML bytes arrive; it applies to resources the page specifically needs that would otherwise be discovered late in the DOM. Early Hints applies to resources the server knows it will use before the HTML is generated. The correct setup uses both.
Is the 103 response cacheable?
RFC 8297 does not define caching semantics for informational responses, and browsers do not cache 1xx responses. Some CDNs cache the Link headers from the origin’s 200 response and use them to generate subsequent 103 responses; in that model the hints are as fresh as the cached 200. If your LCP images or critical CSS change per deployment, ensure your CDN cache invalidation covers the origin 200 responses that carry the Link headers.
What happens if the 103 hints a resource the HTML never uses?
The browser initiates a fetch and stores the result in the preload cache. If the HTML does not reference that resource within an implementation-defined timeout, the cache entry expires and the fetch is wasted. Always hint only resources you are certain the page will use on every load.
Why am I not seeing a 103 in Chrome DevTools?
Chrome DevTools does not display 103 responses as separate entries in the Network panel in all versions. Verify with curl -v --http2 https://example.com/ 2>&1 | grep 103. In DevTools, look for font or CSS fetches starting during the document’s Waiting (TTFB) window; that overlap is the visual proof Early Hints is working.
Does Early Hints help SPAs?
Only for the initial shell request. When the browser first loads the SPA, the index.html or application shell response can benefit from Early Hints for its critical CSS and entry JavaScript bundle. Subsequent client-side route changes do not produce new HTTP requests to the server, so Early Hints has no role in those navigations. For client-side speculative prefetching and prerendering of future navigations, see the Speculation Rules API.
Specification Status
| Technology | Status |
|---|---|
| 103 Early Hints (RFC 8297) | Experimental RFC, December 2017 |
rel=preload in 103 | Chrome 103+, Edge 103+, Firefox 120+. Safari support has historically lagged Chromium and Firefox |
rel=preconnect in 103 | Chrome 103+, Edge 103+, Firefox 120+, Safari 17+ |
rel=prefetch in 103 | Not supported in any major browser |
rel=modulepreload in 103 | Inconsistent; not reliable across browsers |
| NGINX Early Hints proxy passthrough | Since NGINX 1.29.0 (June 2025). Focuses on forwarding upstream responses rather than acting as a standalone hint-generation system |
| HTTP/2 Server Push | Removed in Chrome 106 (2022); effectively obsolete |
19. Glossary
- Server think-time: The duration between a server receiving an HTTP request and beginning to send the response body. Early Hints converts this idle period into productive browser resource fetching.
- TTFB (Time to First Byte): Duration from request start to the first byte of the final HTTP response. Early Hints does not reduce TTFB.
- LCP (Largest Contentful Paint): A Core Web Vitals metric measuring render time of the largest visible viewport element. Primary target of Early Hints optimisation.
- FCP (First Contentful Paint): A Core Web Vitals metric measuring when the first content element (text or image) appears. Improved when critical CSS is preloaded via Early Hints.
- Preload cache: A browser-internal structure storing in-flight or completed fetches initiated by
rel=preload. Resources from103hints are stored here and matched against HTML references by URL and credentials mode. - ALPN (Application-Layer Protocol Negotiation): A TLS extension that negotiates the HTTP protocol version (
h2for HTTP/2,h3for HTTP/3) during the TLS handshake. HTTP/2 requires successful ALPN negotiation. - Preload scanner: A secondary speculative HTML parser, defined in the HTML Living Standard, that reads ahead in the HTML byte stream to discover and fetch resources before the main parser reaches them.
- 1xx informational responses: HTTP response class (100–199) to which
103 Early Hintsbelongs. Informational responses do not terminate the connection or represent a final answer. - fetchpriority: An HTML attribute on
<img>,<link>, and<script>elements, defined in the Priority Hints specification, that signals the relative priority of a resource fetch to the browser’s scheduler. Usefetchpriority="high"on the LCP image.
Revision History
2026-08
- Initial publication. Covers RFC 8297, HTTP/2 protocol requirement, browser preload scanner interaction, fetch priority scheduler, Core Web Vitals impact breakdown, 103 vs Link-in-200 comparison, decision tree, implementation table (corrected Next.js, PHP, Apache, Caddy, WordPress entries), CDN section, double-fetch trap, technique comparison, performance budget, Lighthouse tooling note, troubleshooting table, and 10-item FAQ.
20. References
- RFC 8297 — An HTTP Status Code for Indicating Hints
- 103 Early Hints — MDN Web Docs
- Browser compatibility data — MDN
- HTML Living Standard — Speculative HTML parsing
- Fetch specification — CORS protocol
- Priority Hints specification — WICG
- web.dev — 103 Early Hints
- NGINX changelog — 1.29.0
- Cloudflare — Early Hints documentation
- Fastly Compute documentation
Written by
Platform Engineer and Technical Writer with 10+ years of full-stack development experience and 2+ years focused on DevOps and platform engineering.
