We recently changed how Shopify uses Early Hints on storefronts. Instead of relying only on resource hints added by theme authors, we now scan generated HTML and automatically identify resources that block rendering. We use that information to open third-party connections and start downloading critical files before the browser receives the final HTML response.
Together, these changes produced the following improvements for eligible pageviews at p75:
- First Contentful Paint (FCP) was 76ms faster
- Largest Contentful Paint (LCP) was 100ms faster
The improvement required no changes from merchants or theme developers. Getting there, however, meant revisiting some discouraging earlier results and finding a better way to measure what browsers were doing with Early Hints.
A quick Early Hints primer Jump to heading
A browser cannot discover most page resources until it receives and parses the HTML. Only then can it start downloading the stylesheets and scripts needed to render the page. Meanwhile, it may have spent hundreds of milliseconds waiting for the document response with no knowledge of those files.
The situation can be worse for third-party resources. Before downloading a third-party file, the browser may need to resolve its hostname, open a connection, and negotiate TLS.
Consider this document head:
<link rel="stylesheet" href="/assets/theme.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?[…]">
<script src="https://code.jquery.com/jquery.js"></script>
All three files block rendering. The first cannot start downloading until the browser discovers it in the HTML. The third-party files may also need new connections before their downloads can begin.
Without an interim response, this wait contributes to the page's Time to First Byte (TTFB), and the browser has no information about the resources it will need. A 103 Early Hints response puts part of that time to use. It arrives before the final response and carries Link headers that ask the browser to open a connection or fetch a resource:
HTTP/2 103 Early Hints
Link: <https://fonts.googleapis.com>; rel=preconnect
Link: <//shop.example/cdn/shop/t/1/assets/theme.css>; as=style; rel=preload
HTTP/2 200 OK
Content-Type: text/html
A preconnect asks the browser to set up a connection before it discovers the resource that will use it. A preload goes further and starts downloading the resource itself.
Shopify uses its CDN to serve Early Hints. When we add a Link header to a response, that header gets cached for that URL at the edge. On a later request, the edge node can send a 103 before the request reaches Shopify's application servers. The browser can then open connections and begin downloads while the request travels to those servers and Shopify generates the final HTML. The final response also refreshes the cached metadata.
But a hint helps only if it starts the right work. A poor hint can consume bandwidth, compete with the document response, and slow down rendering.
Our earlier results gave us reason to be cautious Jump to heading
Estela Franco's earlier analysis of Early Hints on Shopify storefronts reported mixed results.
It found modest benefits on desktop when pages used a small number of preloads. Pageviews with larger preload sets often received the main document later. On mobile, pageviews without Early Hints frequently performed better, and performance was worse in groups with more preloads.
There was also a major limitation in how we could measure the feature at the time. We could tell that a browser had received an interim response, but that did not answer the questions we cared about:
- Did the
103contain preloads, or only preconnects? - Which resources were actually early hinted?
- How often did browsers receive each hint?
- Did the page use the preloaded resource, confirming that the preload matched the later request?
Without those answers, we had to group pageviews using indirect signals. We also could not separate a useful preload of a render-blocking file from an unused or non-critical preload. The results still gave us a valid warning about sending too many hints, but they could not tell us whether careful resource selection would change the outcome.
Measuring what the browser actually preloaded Jump to heading
Chrome's experimental Speculative Load Measurement API gave us the missing visibility. It reports which resources were preloaded, whether each preload came from an Early Hints response, and whether the page later used it.
A preload can fail to match the page's request because attributes such as as or crossorigin differ. It can also fetch a resource that the page never requests. In either case, seeing the preload in a 103 does not mean it helped.
Chrome's origin trial gave us enough visibility to test the rollout and measure its impact.
Automatically finding render-blocking resources Jump to heading
The next question was what to hint. Hand-authored preloads are hard to tune: too many can compete with critical requests, while too few can leave critical resources undiscovered until the browser parses the HTML. These hints must also stay in sync as theme markup changes.
Shopify already transforms generated storefront HTML while rendering each response. We extended that mechanism to identify resources on the rendering path.
For stylesheets, we include normal blocking styles and skip non-blocking media such as print. For scripts, we include classic blocking scripts and skip those marked async, defer, or type="module".
This detection reuses an existing HTML scan, so it adds no measurable processing overhead. Deriving hints from generated HTML also keeps them current when a theme adds, removes, or changes a blocking file.
We evaluated each change in a separate experiment. Once a change was live, it became the baseline for the next experiment.
We implemented the work in two steps. First, we opened connections to domains that host third-party resources. Then we used the same information to preload the blocking files themselves.
Step one: automatic third-party preconnects Jump to heading
Our first change identified the domains serving blocking third-party resources. We added up to three of them to the response's Link header alongside Shopify's existing CDN hints:
Link: <https://cdn.shopify.com>; rel=preconnect,
<https://cdn.shopify.com>; rel=preconnect; crossorigin,
<https://code.jquery.com>; rel=preconnect; crossorigin,
<https://fonts.googleapis.com>; rel=preconnect
The browser could then complete connection setup while waiting for the HTML. When parsing later revealed the script or stylesheet, the connection could already be ready to carry its request.
Across buyer traffic, this change made First Contentful Paint 28ms faster and Largest Contentful Paint 36ms faster at p75. At p90, they improved by 56ms and 64ms respectively.
The extra connection hidden inside Typekit Jump to heading
Automatic detection also exposed a less obvious case. A common Adobe Fonts integration loads CSS from use.typekit.net:
<link rel="stylesheet" href="https://use.typekit.net/wws3kbz.css">
That CSS imports another stylesheet from p.typekit.net. The browser discovers the second file only after it downloads and parses the first CSS, adding another serial step to the critical path.
We added a narrow rule. When we find blocking CSS from use.typekit.net, we preconnect to both Typekit domains. On affected pageviews, the additional hint improved First Contentful Paint by another 31ms at p75.
Step two: preload the blocking files themselves Jump to heading
When it completes before resource discovery, a preconnect removes connection setup from the critical path. We next used the same HTML scan to start downloading blocking stylesheets and classic blocking scripts before the document arrived:
Link: <//shop.example/cdn/shop/t/1/assets/theme.css>; as=style; rel=preload,
<//shop.example/cdn/shop/t/1/assets/theme.js>; as=script; rel=preload
On a later request for that URL, the CDN edge can send those links in a 103 Early Hints response. A supporting browser can start fetching the files while Shopify continues producing the HTML.
This differs from a preload added by theme code. We select only files that block rendering in the generated document for that URL. The list updates as we process new responses, reducing the chance that hints become stale or point to work the initial render does not need.
Results from automatic preloads Jump to heading
On pageviews where the browser reported receiving and using our Early Hints preloads, both rendering metrics improved across the distribution:
| Metric | p50 improvement | p75 improvement | p90 improvement |
|---|---|---|---|
| First Contentful Paint | 44ms faster | 76ms faster | 104ms faster |
| Largest Contentful Paint | 68ms faster | 100ms faster | 145ms faster |
We also watched when the final response headers arrived. That timing did not regress, so the browser began useful work earlier without delaying the HTML response. This was our main guardrail for the rollout.
With a 103, TTFB ends when Early Hints arrive, not when the final response begins. We therefore used finalResponseHeadersStart, which marks when the browser receives the first byte of the final response. Improvements generally grew as that timestamp moved later:
Across that range, each additional 100ms until the final response was associated with a 7.5ms larger First Contentful Paint improvement and a 7.6ms larger Largest Contentful Paint improvement at p90. The longer the browser would otherwise wait, the more of a blocking download it can complete in parallel.
Why does LCP improve more than FCP? Jump to heading
We do not know for certain. Our leading explanation is that fetching blocking CSS and JavaScript earlier lets those downloads finish sooner. That leaves more bandwidth available when the browser discovers high-priority images inside the <body>, which often become the Largest Contentful Paint element. We did not preload those images, but they may have faced less competition once their downloads began.
A head start still depends on the buyer's device and network Jump to heading
More time before the final response does not guarantee a larger improvement. The browser also needs enough network and device capacity to use that time, and conditions that delay the HTML can also slow the hinted downloads.
We saw this tension in mobile traffic from India and the United States. Baseline response and rendering times were slower in India, giving Early Hints a longer window before the HTML arrived. If window length were the only factor, improvements would have been larger there. Instead, at p90, we observed a 32ms greater FCP improvement and a 46ms greater LCP improvement in US traffic.
Early Hints change when resource discovery begins. They do not reduce resource sizes or increase the buyer's bandwidth and processing capacity. Their benefit depends on both the size of the available window and how much work the browser can complete during it.
What changed since our earlier study Jump to heading
The earlier study looked at preloads added through theme code, which could include resources beyond those needed for the initial render. For this rollout, Shopify derives a small set of render-blocking CSS and JavaScript from generated HTML, while the new measurement API shows which hints browsers received and used. The results reinforce the earlier guidance: more hints are not inherently better.
Browser support and next steps Jump to heading
Chromium and Firefox can act on preload links in a 103 Early Hints response. Safari accepts the interim response but currently only uses its preconnect hints and ignores preloads. Work is underway in WebKit to support preloads from Early Hints, which would extend these gains to more buyers.
We are also reviewing Shopify's merchant-facing preload controls and guidance. Hand-authored hints remain supported for now as we monitor their impact.
Early Hints deliver the most value when they target resources known to be critical to rendering. Extracting those resources from generated storefront HTML keeps the hints aligned with each page, while browser-side measurement tells us whether they are used and improve performance.
Shopify can do this automatically, without requiring merchants or theme developers to maintain resource hints.