Loading a web page requires fetching a whole set of resources which collectively allow the page to be displayed and interacted with. To increase the user’s perception of speed, you may need to influence the order in which these resources are fetched and executed. And that’s where
preload and Resource Hints come in.
A web page consists of a main HTML document and optional additional resources designed to format it, add media, or behaviors. According to the Web Almanac 2019 (chapter "Page Weight"), more than half the pages contain more than 68 resources added to the main document.
Hence we can assume that under normal circumstances, downloading a resource is a recurring task for the browser. This task can be broken down into several steps, including:
- Revolve the DNS of the resource’s origin (if necessary, i.e. if the browser has not already done so);
- Connect to the origin server (if necessary, i.e. if the browser is not already connected);
- Fetch the resource (if necessary, i.e. if the resource is not already in the user agent cache and still valid);
- And – according to the type of resource and the reason for which it was fetched – its evaluation and use.
For the browser to download resources more efficiently, you can indicate how to optimize these different steps.
You can even tell (ask?) the browser to download some resources in advance, to improve future browsing experience on your site.
To leverage resource loading, you can use
prefetch. We'll call all of these directives "Resource Hints" in the following, but be aware that
preload has its own specification, while the other Resource Hints are defined in a separate specification, mostly because `preload' is not just a hint to the browser, but rather a command.
To write these directives, you can use either the
<link> HTML tag or the
Link HTTP response header. As for specifying the character set, we recommend you to use the HTTP header, since it will be taken into account by the browser before it starts interpreting the HTML code for the page.
In the HTML page body:
<link href="resource-url" rel="directive" as="ressource-type" crossorigin="value" />
Through an HTTP header:
Link: <ressource-url>; rel="prefetch"; as="ressource-type"; crossorigin="value"
Whichever syntax you choose, the parameters are always the same:
- The URL of the resource, of course
- The directive to use
- The type to consider for the resource, which allows you to adapt the Content-Security-Policy to the nature of the resource (learn more)
crossoriginattribute, which we'll discuss later
These settings can be used to define 4 directives supported by most modern browsers:
dns-prefetch: indicates to the browser that it should perform the resolution of a given domain name (determining the IP to contact) before that domain is used to download resources
preconnect: indicates to the browser that it should connect a given origin, before that domain is used to download resources. Preconnecting involves, like -
dns-prefetch, the DNS resolution, but also the TCP handshake and TLS negotiation (if the page is secure)
prefetch: indicates to the browser that it can download a given resource, even if it is not detected in the page. The resource is downloaded with a low priority
preload: tells the browser that it must download a given resource as soon as possible, with high priority.
dns-prefetch can be interesting in the context of critical third parties: by resolving the domain upstream, a few milliseconds can be saved cost-effectively. However, in most cases, you may want to use
preconnect' instead ofdns-prefetch', so as to anticipate the whole connection.
If your site must be compatible with Internet Explorer, which does not support
preconnect, you can use the
rel="preconnect dns-prefetch" syntax .
preload for resources that you know will be useful very early in the page load but that would not naturally be prioritized by the browser. This makes
preload useless for synchronous scripts present in your
<head>, which are already considered by the analyzer to have a high priority. You can use it to increase the priority of execution of an asynchronous script declared in your
preload is also used to load resources referenced by other resources (such as web fonts, images, or other stylesheets referenced using
@import) to retrieve them while the parent resource is fetched and parsed.
prefetch is a directive that sets a very low priority, don’t use it for essential resources. Actually, it can be very relevant for improving navigation on subsequent pages. One of the great use case is to include some
prefetch directives during the authentication of a user, to take advantage of the time it takes them to input their login and password to preload the static resources needed to render the page they will see once authenticated.
crossorigin attribute – which is optional – is used when fetching resources from origins that are different from the main origin (CORS). For many resources (such as CSS, or images), you don't need to specify it, unless you want to request these resources by passing authentication headers, cookies, etc. (with the
For other types of resources, defined in more recent specifications,
crossorigin must be explicitly stated. This is the case, for example, for fonts (see the specification), even when they come from the main origin.
But be aware of the implications: for example, if you are using
preconnect on a third party domain from which you are retrieving images and fonts, make two declarations in order to create two connections: one without
crossorigin, for the images, and the other with it, for the fonts.
Reading this article, you might assume it’s relevant to declare many Resource Hints so that the browser loads the page as quickly as possible to. That is not the case.
Since it sets a low download priority, you can use
prefetch without any real impact on the current page load. Be careful though: its use will increase data consumption on your site, which can present issues both for you (increased traffic on your server), and your users (unnecessary over-consumption of resources).
preload sparingly, as it overrides the priorities set by the browser’s analyzer, both in terms of download and execution. For example, imagine that you have an
async script in your
<head>. Thanks to the
async attribute, its download priority is low. However, once fetched, like any non-deffered script, its parsing and execution interrupt the main thread (you need to use the
defer attribute to change that). If you add a
preload directive matching this resource URL, the browser will fetch it faster, so it will parse it faster, negating the effect of your
async attribute by interuptiing the main thread very early in the page load).
preconnect cost is low, it is not null. If the open connection is not used quickly, then the
preconnect directives only add extra CPU usage (both client and server) and unnecessary network competition.
[…] use it wisely: each open socket incurs costs both on the client and > server, and you want to avoid opening sockets that might go unused. As always, > apply, measure real-world impact, and iterate to get the best performance > mileage from this feature.
"Eliminating Roundtrips with Preconnect [EN]", Ilya Grigorik
This advice from 2015 is still relevant, particularly for connections to secure origins, for which the validation of certificates adds some CPU and network overhead.
The same kind of recommendations can be found in the work of Ivan Akulov, who even advises to switch to
dns-prefetch, which is cheaper, if more than 4 to 6 domains are concerned.
As you can see, with great power comes great responsibility. If you want to get your hands on the priorities generated natively by the browser, then you must proceed in a moderate and responsible way, in order to avoid reproducing network congestion and CPU consumption that would affect your users. This can be very profitable, especially to optimize third party services (analytics, web fonts, etc.).
Like many web performance topics: test, measure and iterate!