Skip navigation
Part IV Chapter 20

Resource Hints

Introduction

Resource hints are instructions to the browser that you may use to improve a website’s performance. This set of instructions enable you to assist the browser in prioritizing origins or resources which need to be fetched and processed.

Let’s take a closer look at how resource hints are implemented, what are the most common pitfalls, and what we can do to make sure we are using resource hints as effectively as possible.

The most widely adopted resource hints are implemented through the Link directive’s rel attribute. These are dns-prefetch, preconnect, prefetch, prerender and preload.

These may be implemented in one of two ways:

HTML element

<link rel="dns-prefetch" href="https://example.com">

HTTP header

Link: <https://example.com>; rel=dns-prefetch

It is also possible to dynamically inject the HTML element through the use of JavaScript:

const link = document.createElement("link");
link.rel="prefetch";
link.href="https://example.com";
document.head.appendChild(link);

Adoption for HTTP headers is significantly lower than having resource hints implemented as part of the document markup; with less than 1.5% of the pages analyzed implementing resource hints through HTTP headers. This is likely attributed to the ease with which they may be added or modified from within the HTML source, when compared to adding an HTTP header on the server.

Figure 20.1. Popularity of resource hints as HTTP headers and HTML markup.

Using our current methodology, it is not possible to reliably measure resource hints that are added following user-interaction, such as those added through QuickLink, though that particular library featured on less than 0.1% of pages analyzed, according to the Core Web Vitals Technology Report.

Considering that the adoption of resource hints using HTTP headers is markedly smaller than adoption for the <link> HTML element, the rest of this chapter will focus on analyzing the usage of resource hints through the HTML element.

Types of resource hints

There are five resource hint link relationships supported by most browsers today: dns-prefetch, preconnect, prefetch, prerender and preload.

dns-prefetch

<link rel="dns-prefetch" href="https://example.com/">

The dns-prefetch hint initiates an early request to resolve a domain name. It is only effective for DNS lookups on cross-origin domains and may be paired together with preconnect. While Chrome now supports a maximum of 64 concurrent in-flight DNS requests—up from 6 last year—other browsers still have tighter limitations. For example, it is limited to 8 on Firefox.

preconnect

<link rel="preconnect" href="https://example.com/">

The preconnect hint behaves similarly to dns-prefetch, but in addition to DNS lookups, it also establishes a connection together with TLS handshake if served over HTTPS. You are able to use preconnect in place of dns-prefetch as it gives a greater performance boost; but you must use it sparingly as certificates are usually upwards of 3 KB, which would be competing with bandwidth for other resources. You also want to avoid wasting CPU time opening connections which aren’t required for critical resource. Keep in mind that if a connection isn’t used within a short period of time (e.g., 10 seconds on Chrome), it would automatically be closed by the browser, wasting any preconnect effort.

prefetch

<link rel="prefetch" href="/library.js" as="script">

The prefetch hint allows you to recommend to the browser that a resource might be required by the next navigation. The browser may initiate a low-priority request for the resource, possibly improving the user experience as it would be fetched from the cache when needed. While resource may be fetched in advanced with prefetch, it will not be preprocessed or executed until the user navigates to the page which requires the resource.

prerender

<link rel="prerender" href="https://example.com/page-2/">

The prerender hint allows you render a page in the background, improving its load time if the user navigates to it. In addition to requesting the resource, the browser may preprocess and fetch and execute subresources. prerender could end up wasteful if the user does not navigate to the prerendered page. Contrary to the specification, Chrome treats the prerender hint as a NoState Prefetch to reduce this risk. Unlike a full prerender it won’t execute JavaScript or render any part of the page in advance but only fetch the resources in advance.

preload

Most modern browsers also support the preload hint—and to a lesser degree, the modulepreload hint. The preload instruction initiates an early fetch for a resource which is required in the loading of a page and is most commonly used for late-discovered resources, such as font files or images referenced in stylesheets. Preloading a resource may be used to elevate its priority, allowing the developer to prioritize the loading of the Largest Contentful Paint (LCP) image for, even if this would otherwise be discovered while parsing the HTML.

modulepreload is a specialized alternative to preload and behaves similarly, however its usage is limited to module scripts.

Figure 20.2. Adoption of the link rel attribute.

The most widely used resource hint is dns-prefetch (36.4% on mobile); which is unsurprising, considering it was introduced in 2009. With the widespread use of HTTPS, in many cases you should replace it with preconnect (12.7% on mobile), if you are certain that you will be connecting to that domain. Considering that the preload hint is comparatively new, first appearing in Chrome in 2016, it is the second most widely adopted resource hint (22.1% on mobile) and is seeing constant growth year-on-year—a testament to the importance and flexibility of this directive.

As shown in the charts above, the adoption rates on mobile and desktop are near-identical.

By rank

Figure 20.3. Adoption of rel="preload" segmented by CrUX rank.

You can observe that when segmenting the data by rank, the adoption rates change notably, with the preload hint increasing from 22.1% for our whole data set, to claim the top spot with an adoption rate of 44.3% amongst the top 1,000 sites.

Figure 20.4. Adoption of rel="dns-prefetch" segmented by CrUX rank.

dns-prefetch is the only resource hint which exhibits a decrease in adoption when comparing the top 1,000 sites with the overall adoption.

Figure 20.5. Adoption of rel="preconnect" segmented by CrUX rank.

To counter this decrease, the top 1,000 pages have an increased adoption for the preconnect hint, taking advantage of its increased performance boost and wide support. I expect that the adoption for preconnect will continue increasing as the rest of the internet follow suit.

Usage

Resource hints can be very effective if used correctly. By shifting the responsibility from the browser to the developer, it allows you to prioritize resources required for the critical rendering path and improve the load times & user experience.

Rank pre­load pre­fetch pre­connect pre­render dns-prefetch module­preload
1,000 3 2 4 0 4 1
10,000 3 1 4 1 3 1
100,000 2 2 3 1 3 1
1,000,000 2 2 2 1 2 1
all 2 2 1 1 2 1
Figure 20.6. Median number of resource hints per page by rank.

Of the sites using resource hints, when comparing the median for the top 1,000 sites to the entire corpus, the top-ranking sites have more resource hints per page. The only hint which observes a different pattern is prerender, which has a total of 0 occurrences in the top 1,000 sites.

Correlation with Core Web Vitals

Figure 20.7. Correlation between good CWV score and number of rel="preload" hints

By combining a page’s Core Web Vitals scores in the CrUX dataset and the usage of the preload resource hint, you can observe a negative correlation between the number of link elements and the percentage of pages which score a good rating on CWV. The pages which use fewer preload hints are more likely to have a good rating.

Figure 20.8. Correlation between good LCP score and number of rel="preload" hints

This same observation may be seen on a page’s LCP, indicating that in many cases, the developer is prioritizing resources which aren’t needed to render the LCP element and as a consequence degrading the user experience.

While this doesn’t prove that having preload hints causes a page to get slower, having many hints does correlate with having slower performance. Every page has its unique requirements and it is impossible to apply a “one size fits all” approach, but in the majority of cases the number of preloaded resources should be kept low and resource prioritization should be delegated to the browser when possible.

Note: In addition to the number of hints, the size of each preloaded resource has an impact on the website performance. The above figure does not take into consideration the size of each preloaded resource.

rel="preload"

With that being said, and the expectation that more websites will adopt preload, let’s take a better look at the preload resource hint and understand why it is so effective, yet at the same time so prone to misuse.

The as attribute

The as attribute should be specified when using rel="preload" (or rel="prefetch") to specify the type of resource being downloaded. Applying the correct as attribute allows the browser to prioritize the resource more accurately. For example, preload as="script" will get a low or medium priority, while preload as="style" would be assigned an internal request priority of Highest. The as attribute is required for caching the resource for future requests and applying the correct Content Security Policy.

Figure 20.9. rel="preload" as attribute values.

script

script is the most common value by a significant margin. <script> elements are usually discovered early as they are embedded in the initial HTML document, but it is a common practice to place <script> elements before the closing <body> tag. Since HTML is parsed sequentially, this means that the scripts will be discovered after the DOM is downloaded and parsed—and with more websites dependent on JavaScript frameworks, the necessity to have JavaScript load early has increased. The downside is that JavaScript resources would be prioritized over the other resources discovered within the HTML document, including images and stylesheets, possibly compromising the user experience.

font

The second most commonly preloaded resource is the font, which is a late-discovered resource since the browser will only download a font file after the layout phase when the browser knows that the font will be rendered on the page.

style

Stylesheets are ordinarily embedded in the document’s <head> and discovered early during the document parsing. Additionally, as stylesheets are render-blocking resources they are assigned the Highest request priority. This should make preloading stylesheets unnecessary, but it is sometimes required to re-prioritize the requests. A bug in Google Chrome (fixed in Chrome 95) prioritizes preloaded resources ahead of other higher-priority resources discovered by the preload scanner, including CSS files. Preloading the stylesheet will restore its Highest priority. Another instance when stylesheets are preloaded is when they are not downloaded directly from the HTML document, such as the asynchronous CSS “hack” which uses an onload event to avoid render-blocking the page with non-critical CSS.

fetch

Preload may be used to initiate a request to retrieve data which you know is critical to the rendering of the page, such as a JSON response or stream.

image

Preloading images may help improve the LCP score when the image is not included in the initial HTML, such as a CSS background-image.

The crossorigin attribute

The crossorigin attribute is used to indicate whether Cross-Origin Resource Sharing (CORS) must be used when fetching the requested resource. This could apply to any resource type, but it is most commonly associated with font files as they should always be requested using CORS.

value desktop mobile
not set 66.6% 65.9%
crossorigin (or equivalent) 14.5% 13.5%
use-credentials < 0.1% < 0.1%
Figure 20.10. rel="preload" crossorigin attribute values.

anonymous

The default value when no value is specified is anonymous and this value will set the credentials flag to same-origin. It is required when downloading resources protected by CORS. It is also a requirement when downloading font files—even if they are on the same origin! If you omit the crossorigin attribute when the eventual request for the preloaded resource uses CORS, you will end up with a duplicate request since it won’t match in the preload cache.

use-credentials

When requesting cross-origin resources which require authentication, for example through the use of cookies, client certificates or the Authorization header; setting the crossorigin="use-credentials" attribute will include this data in the request and allow the server to respond to the request so that the resource may be preloaded. This is not a common scenario with 0.1% usage, however if your page content is dependent on an authenticated status, it could be used to initiate an early fetch request to get the login status.

The media attribute

An oft-neglected feature available to rel="preload" is the ability to specify media queries through the media attribute—with less than 4% of all preloads using this attribute. The media attribute accepts media queries allowing you to target the media type and specific browser features, such as viewport width. As an example, the media attribute would allow you to preload a low-resolution image on devices with a narrow viewport and a full-sized image on devices with a large viewport.

In addition to the media attribute, the <link> element supports imagesrcset and imagesizes attributes which correspond to the srcset and sizes attributes on <img> elements. Using these attributes, you can use the same resource selection criteria that you would use on your image. Unfortunately, their adoption is very low (less than 1%); most likely owing to the lack of support on Safari.

Note: The media attribute is not available on all <link> elements as the spec suggests, but it is only available on rel="preload".

Bad practices

Owing to the versatility of rel="preload", there isn’t a clear set of rules dictating how to implement the preload hint, but we can learn a lot from our mistakes and understand how to avoid them.

Unused preloads

We have already seen that there is a negative correlation between a website’s performance and the number of preload hints. This relationship may be influenced by two factors:

  • Incorrect preloads
  • Unused preloads

An incorrect preload refers to when you preload a resource which is not as important as the other resources which the browser would have otherwise prioritized. We are unable to measure the extent of incorrect preloads as you would need to A/B test the page with and without each hint.

An unused preload occurs when you preload a resource which is not needed within the first few seconds of loading the page.

21.5%
Figure 20.11. Percent of unused preload hints within the first 3 seconds.

In such cases, the preload hint is regressing the website’s performance, as you are instructing the browser to download and prioritize files or resources which are not needed immediately—or even not needed at all. This is one of the challenges when using resource hints, as they require regular maintenance and automating the process opens the door to allow such issues to creep in.

Incorrect crossorigin attribute

Attempting to preload a CORS-enabled resource without including the correct crossorigin attribute will download the same resource twice. The crossorigin attribute is required on the <link> element if the eventual request would also use CORS. This is also the case when requesting font files, even when self-hosting font files on the same origin, as font files are always treated as CORS-enabled.

Figure 20.12. Percent of incorrect crossorigin values segmented by file extension on mobile devices.

More than half (63.6%) of the cases when the crossorigin attribute on the rel="preload" hint is either missing or incorrect, are linked to the preloading of font files, with a total of 14,818 instances across the dataset.

Invalid as attribute

The as attribute plays an important role when preloading your resources and getting this wrong may result in downloading the same resource twice. On most browsers, specifying an unrecognized as attribute will ignore the preload. The supported values are audio, document, embed, fetch, font, image, object, script, style, track, worker and video.

There are 17,861 cases of unrecognized values, with the most frequent error being omitting it completely; while the most common invalid as values are other and stylesheet (the correct value is style).

1,114
Figure 20.13. Pages incorrectly used as="stylesheet" instead of "style"

When using an incorrect as attribute value—as opposed to unrecognized value, such as using style instead of script—the browser will duplicate the file download as the request won’t match the resource stored in the preload cache.

Note: While video is included in the spec, it isn’t supported by any browser and would be treated as an invalid value and ignored.

Unused font files

More than 5% of pages which preload font files preload more font files than needed. When preloading font files, all browsers which support preload also support .woff2. This means that, assuming that the .woff2 font files are available, it is not necessary to preload older formats, including .woff.

Third parties

You can use resource hints to connect to, or download resources from, both first and third parties. While dns-prefetch and preconnect are only useful when connecting to different origins, including subdomains, preload and prefetch may be used for both resources on the same origin and resources hosted by third parties.

When considering which resource hints you should use for third-party resources, you need to evaluate the priority and role of each third party on your application’s loading experience and whether the costs are justified.

Prioritizing third-party resources over your own content is potentially a warning sign, however there are cases when this is recommended. As an example, if we look at cookie notice scripts—which are required in the European Union by General Data Protection Regulation—these are usually accompanied by a dns-prefetch or preconnect hint as they are highly obtrusive to the user experience and also a prerequisite for some site functions, such as serving personalized ads.

host dns-prefetch preconnect preload Total
adservice.google.com 0.2% 0.5% 35.7% 36.4%
fonts.gstatic.com 0.9% 24.0% 0.6% 25.5%
fonts.googleapis.com 14.0% 4.5% 2.7% 21.2%
s.w.org 19.7% 0.2% - 19.9%
cdn.shopify.com - 1.7% 9.6% 11.2%
siteassets.parastorage.com - - 5.9% 5.9%
www.google-analytics.com 1.2% 3.9% 0.2% 5.3%
www.googletagmanager.com 1.9% 2.7% 0.2% 4.8%
static.parastorage.com - - 4.7% 4.7%
ajax.googleapis.com 2.2% 1.6% 0.3% 4.1%
www.google.com 2.7% 1.0% 0.1% 3.8%
images.squarespace-cdn.com - 3.5% - 3.5%
cdnjs.cloudflare.com 1.6% 1.0% 0.4% 2.9%
monorail-edge.shopifysvc.com 2.0% 0.8% - 2.8%
fonts.shopifycdn.com - 1.1% 1.0% 2.1%
Figure 20.14. Most popular third-party connections using resource hints on mobile devices.

Analyzing the table above, 36.7% of all pages which include a preload hint are preloading resources hosted on adservice.google.com. The s.w.org host is the most popular domain for dns-prefetch and is used on WordPress sites (since version 4.6) for the loading of SVG images from its Twemoji CDN, when the browser is detected to not support native emoji characters. Google Fonts related services on fonts.gstatic.com and fonts.googleapis.com are the two most popular hosts for the preconnect directive.

Google Fonts instructions to preconnect to fonts.gstatic.com and fonts.googleapis.com. (Source: Google Fonts)
Figure 20.15. Google Fonts instructions to preconnect to fonts.gstatic.com and fonts.googleapis.com. (Source: Google Fonts)

Google Fonts now includes instructions to preconnect to both the fonts.gstatic.com origin and fonts.googleapis.com, which is usually good practice to offset the impact of these late discovered resources.

To learn more about the state of third parties, check out the Third Parties chapter.

Native lazy-loading

Lazy-loading refers to the technique to defer downloading a resource—in this case an image or iframe—until it is needed or visible within the viewport. Native lazy-loading refers to the ability to specify this in the HTML with a loading="lazy" attribute, rather than having to use a JavaScript library to handle this. Native image and iframe lazy-loading have been standardized in 2019 and since then their adoption—especially for images—has grown exponentially.

loading="lazy" for images is supported on most major browsers. On Safari, it is marked as in progress and is available behind a flag, but not yet enabled by default.

Lazy-loading of iframes is supported on Chrome, once again behind a flag on Safari but not yet supported on Firefox.

Browsers which do not support the loading attribute will simply ignore it—making it safe to add without unwanted side-effects. JavaScript based alternatives, such as lazysizes may still be used, however considering that full browser support is around the corner, it may not be worth adding to a project at this stage.

Figure 20.16. The percent of pages that have the loading="lazy" attribute on img elements.

The percent of pages using loading="lazy" has grown from 4.2% in 2020 to 17.8% by the time of our analysis. That’s a whopping 423% growth! This rapid growth is extraordinary and is likely driven by two key elements: the ease with which it could be added to pages without cross-browser compatibility issues, and the frameworks or technologies powering these websites. In WordPress 5.5, lazy-loading images became the default implementation, supercharging the adoption rate of loading="lazy", with WordPress sites now making up 84% of all pages which use native image lazy-loading.

Figure 20.17. Percent of img elements with loading="lazy" which are in the initial viewport.

61.5% of lazy-loaded images on mobile and 63.1% of lazy-loaded images on desktop are actually within the initial viewport and shouldn’t be lazy-loaded. A study on the load times for pages which use lazy-loading indicated that pages which use lazy-loading tend to have a worse LCP performance, possibly caused by overusing the lazy-loading attribute. This is increasingly significant on the LCP element, which shouldn’t be lazy-loaded. If you are using loading="lazy", you should check that the lazily-loaded images are below the fold and more critically, that the LCP element is not lazy-loaded. You may dig deeper into the effects of lazy-loading the LCP image on your Core Web Vitals in the Performance chapter.

2.6%
Figure 20.18. Percent of pages that have the loading="lazy" attribute on iframe elements.

The likelihood of a page containing at least one iframe is much lower than for that containing an image with only 2.6% of pages containing an iframe taking advantage of native lazy-loading. The benefits of lazy-loading an iframe are potentially important, as an iframe could initiate further requests to download even more resources, including scripts and images. This is especially true when using embeds, such as YouTube or Twitter embeds. Similarly, when deciding the loading strategy for an image, you must check whether the iframe is shown within the initial viewport or not. If it isn’t, then it is usually safe to add loading="lazy" to the <iframe> element to benefit from a reduced initial load and boost performance.

HTTP/2 Server Push

HTTP/2 supports a technology called Server Push that preemptively pushes a resource it expects the client will be requesting. As the server is pushing the resource instead of informing the client that it should request it, cache-management becomes complex and, in some cases, the pushed resources would even delay the delivery of the HTML, which is critical for discovering all resources required to load the page.

Unfortunately, HTTP/2 push has been disappointing, with little evidence that it provides the performance boost promised compared to the risk of over pushing resources that either the browser already has, or that are of less importance than resources the browser requests.

So, while the technology is widely available, overcoming these obstacles makes it highly unpopular—with less than 1% adoption. Chrome has also filed an Intent to Remove that is paused until a testable implementation of 103 Early Hints (covered next) is available. Chrome does not support Server Push on HTTP/3 either.

You can read more about HTTP, HTTP/2, and HTTP/3 in the HTTP chapter.

Future

While there are no proposals to add new rel directives, improvements from the browser vendors to the current set of resource hints—such as the prioritization bug in Chrome—are expected to have a positive impact. Hint adoption is expected to evolve, and the use of preload should shift towards its intended purpose: late discovered resources.

Additionally, two proposals, 103 Early Hints and Priority Hints, are expected to be made available soon, with experimental support already available on Chrome.

103 Early Hints

Chrome 95 added experimental support for 103 Early Hints for preload and preconnect. Early hints enable the browser to preload resources before the main response is served and take advantage of the idle time on the browser between the request being sent and the response from the server. When using 103 Early Hints, the server immediately sends an “informational” response status detailing the resources to be preloaded using the HTTP header method, while processing the real document response. This way, the browser will be able to initiate preload requests for critical resources even before the HTML arrives and much earlier than it would if using the <link> element in the document markup. 103 Early Hints overcomes most of the difficulties encountered with HTTP/2 Server Push.

Priority Hints

Priority hints inform the browser of the relative importance of resources within the page, intending to prioritize critical resources and improve Core Web Vitals. Priority Hints are enabled through the document markup by adding the importance attribute to resources, such as <img> or <script>. The importance attribute accepts an enumeration of high, low or auto and by combining this with the type of resource, the browser would be able to assign the optimal fetch priority based on its heuristics. Priority Hints are available on Chrome 96 as an origin trial.

Conclusion

During the past year, resource hint adoption grew and is expected to continue growing as developers take advantage of these APIs to prioritize resources and improve the user’s experience. At the same time, browser vendors have continued calibrating these directives, evolving their role and effectiveness.

Resource hints could become a double-edged sword if the benefit for your users is not evaluated. Almost a quarter of preload requests went unused while the number of preload hints correlated with slower load times.

Resource hints are akin to fine-tuning a race car’s engine. They would not turn a slow engine into a fast one, and too many adjustments could break it. Yet, some small tweaks here and there would allow you to maximize it.

So once again, the mantra behind resource hints remains, “if everything is important, then nothing is”. Use resource hints wisely and don’t overuse them.

Author