Early Hints (103) on Cloudflare
Between the moment a browser sends a request for a page and the moment the first byte of HTML arrives, the connection is idle. For a static page served straight from a CDN cache, that gap is tiny. For a cache miss fetched from a distant origin, or a page that passes through an edge function, it can be 100–400 ms — time in which the browser already knows, or could know, that it will need the site's stylesheet, a font and a hero image. HTTP 103 Early Hints lets the CDN tell the browser about those resources during the gap, so it starts fetching them before the HTML arrives.
This guide enables Early Hints on Cloudflare for a static documentation site whose HTML is sometimes fetched from an S3 origin, decides which resources to hint, verifies that browsers receive and use the hints, and measures when they help and when they do not. It is part of Resource Hints and Navigation Speed.
Prerequisites
- A site proxied through a Cloudflare zone (or a Pages project, where Early Hints are on by default).
- The critical resources for each template identified: main CSS, LCP image, critical font.
- Field or lab data that shows a gap between navigation start and first byte (TTFB).
How Cloudflare Produces Early Hints
Early Hints need the resources to be known before the final response is ready. Cloudflare solves this by remembering them: when a response for a URL includes Link headers with rel=preload or rel=preconnect, Cloudflare caches those headers; on a later request for the same URL, it immediately sends a 103 with them, then fetches or serves the final response. The HTML itself does not need to be cached for this to work, which is what makes it useful on cache misses.
For Pages projects, Cloudflare also generates Link headers automatically from <link rel="preload"> and <link rel="preconnect"> elements in the HTML. For a zone in front of another origin, the origin (or a Transform Rule) must send the Link headers.
Step 1: Send Link Headers From the Origin
The site's HTML lives in S3, which cannot vary headers per page on its own. Two options: set Link as S3 object metadata during upload (per page, from the build), or add them with a Cloudflare Transform Rule (per template, by path). The build already knew each page's critical resources, so it wrote them as metadata at upload time:
# during deploy, per HTML file (generated from the build manifest)
aws s3 cp dist/docs/guide/proxy/index.html s3://docs-bucket/docs/guide/proxy/index.html \
--content-type 'text/html; charset=utf-8' \
--metadata-directive REPLACE \
--cache-control 'public, max-age=0, s-maxage=300, stale-while-revalidate=86400' \
--metadata 'link=</_astro/base.Df3k2a.css>; rel=preload; as=style, </fonts/inter-400-700.woff2>; rel=preload; as=font; type="font/woff2"; crossorigin'
S3 returns user metadata as x-amz-meta-link, so a Cloudflare Transform Rule renames it to Link on the response. A simpler alternative for sites where every page shares the same critical CSS and font is a single Transform Rule that sets a static Link header for all HTML paths.
Step 2: Enable Early Hints and Verify
Enable Early Hints in the zone's Speed → Optimization settings (or early_hints: on via the API). Then verify with curl, which prints informational responses:
curl -sI --http2 https://docs.example.com/docs/guide/proxy/ | sed -n '1,6p'
# HTTP/2 103
# link: </_astro/base.Df3k2a.css>; rel=preload; as=style
# link: </fonts/inter-400-700.woff2>; rel=preload; as=font; type="font/woff2"; crossorigin
#
# HTTP/2 200
The first request after enabling returns only the 200 — Cloudflare has to see the Link header once before it can replay it. In Chrome DevTools, resources fetched because of an Early Hint show "early-hints" in the Initiator column. Chrome and Edge support Early Hints for navigations over HTTP/2 and HTTP/3; Firefox added support in version 120; Safari supports preconnect hints from 103 but not preloads at the time of writing.
Step 3: Choose What to Hint
Hints compete with each other and with the HTML for bandwidth. The site hints at most three resources per page:
- The main stylesheet — render-blocking, so it directly delays first paint.
- The body font — often the next thing that blocks text rendering.
- The LCP image — only on templates where the LCP element is an image (landing pages), with
fetchpriority=highin the hint.
Hinting all fonts, all scripts and several images was tested and made LCP worse on slow connections: the extra downloads competed with the HTML and with the LCP image. The same restraint applies to regular preloads, as in Auditing Unused Preloads.
Preconnect Hints for Third-Party Origins
Early Hints can carry rel=preconnect as well as preloads, and for pages that must fetch from a third-party origin early — a font service, an image CDN on another hostname, an API the first interaction calls — a preconnect hint in the 103 lets the browser complete DNS, TCP and TLS for that origin during the idle time. The documentation site had one such origin, its image CDN on a separate hostname for screenshots. Adding Link: <https://img.example.net>; rel=preconnect to the hints cut the connection setup for the first screenshot by about 110 ms on cache-miss navigations. Safari, which ignores preloads in 103 responses, honours preconnects, so this was also the one Early Hints benefit Safari readers received. As with preloads, keep preconnects to the one or two origins the page genuinely needs early; each open connection has a cost on constrained devices.
When Early Hints Help and When They Do Not
Early Hints fill idle time before the HTML. They help in proportion to that idle time:
| Scenario | Idle time before HTML | LCP change with hints |
|---|---|---|
| HTML edge cache hit, reader near edge | ~5 ms | none measurable |
| HTML edge cache miss, origin in same region | ~40 ms | −30 ms |
| HTML edge cache miss, origin far away | ~240 ms | −170 ms |
| HTML through an edge function (auth check) | ~90 ms | −70 ms |
Once HTML caching at the edge was improved, as described in Improving TTFB for Static HTML, about 95% of navigations were edge hits and saw no benefit. Early Hints are therefore a complement to edge caching, not a substitute: they make the remaining misses less painful.
Measured Impact
Two weeks of field data with Early Hints off and on, split by HTML cache status (recorded via a Server-Timing header):
| Measure | Hints off | Hints on |
|---|---|---|
| LCP p75, HTML cache misses (5% of views) | 2.9 s | 2.7 s |
| LCP p75, HTML cache hits (95% of views) | 1.52 s | 1.51 s |
| FCP p75, cache misses | 1.9 s | 1.6 s |
| Share of navigations receiving a 103 | — | 81% (Chromium + Firefox) |
| Wasted hinted bytes (resource not used) | — | 0 (only critical resources hinted) |
Pitfalls & Rollback
- Expecting gains on cached HTML. There is no idle time to fill; fix edge caching first.
- Hinting too many resources. Extra downloads compete with the HTML and the LCP image.
- Hints for resources the page no longer uses. Stale
Linkmetadata after a redesign wastes bandwidth; generate hints from the build. - Missing
crossoriginon font hints. The hinted font is fetched twice. - Rollback: disable Early Hints in the zone settings; the
Linkheaders can stay, since they also act as normal preload headers on the final response.
Conclusion
Early Hints let a CDN put the time before the HTML to work, starting downloads of the stylesheet, a font and the LCP image while the page is still on its way. On Cloudflare they are replayed from Link headers the site already sends. On a static site whose HTML is almost always cached at the edge, they change little; on the navigations that miss the cache or pass through an edge function, they cut hundreds of milliseconds of idle time. Hint only the critical few resources, generate the headers from the build, and treat them as a complement to good edge caching.
FAQ
What is a 103 Early Hints response?
An informational HTTP response sent before the final response, carrying Link headers for resources the page will need. Browsers that support it start preloading or preconnecting to those resources while the server or CDN is still preparing the final HTML.
Do static sites benefit from Early Hints?
Mostly when the HTML is not served instantly, such as on edge cache misses, when HTML is fetched from a distant origin, or when an edge function runs before responding. When HTML comes straight from the edge cache in a few milliseconds, there is little idle time for hints to fill.
How does Cloudflare generate Early Hints?
When Early Hints is enabled for a zone, Cloudflare caches Link headers with preload or preconnect from responses and sends them as a 103 on later requests for the same URL, before fetching or serving the final response. Pages projects also generate Link headers automatically from preload elements in the HTML.
Which resources should be hinted?
Only a few critical ones: the main stylesheet, the LCP image, and critical fonts, plus preconnect for any required third-party origin. Hinting many resources competes with the HTML itself and with each other.
Related
- Parent: Resource Hints and Navigation Speed — all resource hints.
- Preconnect vs DNS-Prefetch on Static Sites — connection hints.
- Fetchpriority Beyond the Hero Image — priority once resources are known.
- Preloading Fonts Without Double Downloads — the crossorigin detail.
- Cloudflare Pages Functions Middleware — edge code that creates idle time.
Filed underHow-tosCloudflare