Dns-prefetch & Preconnect: 7 Tips, Tricks and Pitfalls
Mastering the not so obvious parts of dns-prefetch & preconnect
The first time I worked with resource hints things just didn’t work as expected. Real user monitoring (RUM) metrics were not showing any of the promised improvements when fetching resources. Information was all over the place and some of the trickiest edge cases were hidden in the high volume of articles available.
In this article, I want to put the most important concepts in one place, so that if you are in the same situation, you have at least one place to start with
preconnect. There are more resource hints such as
preload, but I won’t cover them in this article.
If you don’t know what
dns-prefetch are, here is a short introduction. Feel free to skip it if you already know what they are and jump to Tips, tricks and pitfalls section. At the bottom you can find an extensive list of references supporting the main topics described in the article.
When the browser parses your HTML, it fetches all the resources such as fonts, stylesheets and scripts if they are not already cached. That means that the browser has to reach the line of code where your resource is declared in your HTML, resolve the DNS of the resource and establish a connection with that host to download the asset.
As the previous image shows, there is a sequential order followed by the browser to download resources.
What if we could tell the browser that there is going to be a connection with a specific host in the short future? This is what
preconnect is all about:
The preconnect link relation type is used to indicate an origin that will be used to fetch required resources. Initiating an early connection, which includes the DNS lookup, TCP handshake, and optional TLS negotiation, allows the user agent to mask the high latency costs of establishing a connection — Source: W3C.org
If we know some resources are going to be fetched from specific hosts but we don't know exactly what resources we will download, we can save some time by establishing a connection using
preconnect. We can either specify it in the document markup or via the HTTP Link header:
<link rel="preconnect" href="https://www.vrbo.com">
- Link header:
Link: <https://www.vrbo.com>; rel=preconnect
We might be interested in doing the dns lookup only. In this case we can use
The dns-prefetch link relation type is used to indicate an origin that will be used to fetch required resources, and that the user agent SHOULD resolve as early as possible. — Source: W3C.org
dns-prefetch can either be specified in the document markup or via the
HTTP Link header:
<link rel="dns-prefetch" href="//example.com">
- Link header:
Link: <https://www.vrbo.com>; rel=dns-prefetch
Main difference between dns-prefetch and preconnect
This is the simplest way of looking at these two:
dns-prefetch: DNS lookup
preconnect: DNS lookup + TCP handshake + TLS negotiation
dns-prefetch just warms up the local cache and it doesn’t tie up any resources. However,
preconnect will maintain a connection with the specified origin. As this is a costly operation, we should only use
preconnect for origins we will surely connect to.
Tips, tricks and pitfalls
In this section we’ll cover the not so obvious parts related to
1. Auto dns-prefetch for hyperlinks
Some browsers do a
dns-prefetch for the hyperlinks displayed in your
HTML, so that when the user clicks on it there is no additional latency added to the
DNS resolution. An important point to take into account is that Chrome does not
dns-prefetch hyperlinks in
HTTPS pages by default:
By default, Chromium does not prefetch host names in hyperlinks that appear in HTTPS pages. This restriction helps prevent an eavesdropper from inferring the host names of hyperlinks that appear in HTTPS pages based on DNS prefetch traffic. The one exception is that Chromium may periodically re-resolve the domain of the HTTPS page itself. — Source: chromium.org
As stated in this MDN entry, you can enable/disable this functionality in Chrome and Firefox by adding a
X-DNS-Prefetch-Control header. It can also be set up through a meta tag:
<meta http-equiv="x-dns-prefetch-control" content="on">.
This option should only be enabled if we know for sure it will not interfere with the user event tracking system. Otherwise users might be registered for visiting links they have not clicked on.
2. Use dns-prefetch as a fallback for preconnect
If we check on caniuse.com, we can see that more browsers support
- dns-prefetch: https://caniuse.com/#search=dns-prefetch
- preconnect: https://caniuse.com/#search=preconnect
preconnect links can be declared followed by a
dns-prefetch link so that if the first one is not supported, time can be saved for the DNS resolution of the origin:
<link rel="preconnect" href="https://example1.com">
<link rel="preconnect" href="https://example2.com">
<link rel="preconnect" href="https://example3.com"><link rel “dns-prefetch” href=”https://example1.com">
<link rel=”dns-prefetch” href=”https://example2.com">
<link rel=”dns-prefetch” href=”https://example3.com">
3. Cross-Origin Resource Sharing (CORS)
If our resource is going to be fetched through a
CORS connection using
anonymous mode, we need to add the attribute
crossorigin to the link tag:
<link rel="preconnect" href="https://cdn.vrbo.com" crossorigin>
As stated in MDN article,
CORS is used to enable cross-site
HTTP requests for:
XMLHttpRequest/ Fetch APIs in a cross-site manner.
- Web Fonts (for cross-domain font usage in
- Images/video frames drawn using
- CSS Shapes from images.
One of the most common mistakes is to set a
CORS connection by using the attribute
crossorigin and then fetching a
non-CORS asset type such as a script.
Ilya Grigorik explains in his article how this simply won’t work as
non-CORSresources are downloaded using separate connections. There is more information about this topic on this great article written by Michael Crenshaw.
Sometimes, especially when we fetch resources from a
CDN, we fetch
As the image above shows, we are establishing two different connections to cdn.vrbo.com. We can set up two
preconnect hints, one of them with a
<link rel="preconnect" href="https://cdn.vrbo.com">
<link rel="preconnect" href="https://cdn.vrbo.com" crossorigin>
We can see that following this approach, we will set up these two connections at the same time:
4. The browser closes unused connections
On this Google Developers article as well as on Milica Mihajlija’s Google Developers entry, we can see that unused connections are closed after ten seconds of inactivity. That means that if it takes more than ten seconds to find and start fetching a resource, all work done to
preconnect with the origin could be wasted.
5. Number of connections
Opening a connection is an expensive operation. Though he doesn’t offer the data in this article to support his guidance, we can keep in mind the advice of Ivan Akulov. On his blog, he recommends to only use
preconnect when we’re certain we’ll use the connection, with no more than 4-6 domains. And we can still use
dns-prefetch for other connections.
6. Browsers can ignore resource hints
preload is mandatory for browsers at this time. All other resource hints are still in the working draft stage and a browser can ignore them if it chooses to do so.
The browser could choose not to set up a new connection if there are already too many connections open. You shouldn’t expect a given
preconnect to work just because you asked for it.
7. Test your hypothesis, you can make things worse
As we explained at the beginning of this article, adding resource hints might help to reduce the time needed to download resources from hosts. The best benefit of resource hints is that by the time the browser discovers a resource, a connection is already in place to fetch it.
However, as we remove connection times when some resources are found, we could end up changing the order in which resources are fetched and processed. In some cases that could affect some of your Core Web Vitals metrics, causing performance degradation.
Always try to understand how your resources are loaded and test your hypothesis based on that analysis.
A great tool to test your loading flow is WebPageTest. Andy Davies explains in his article how to inject scripts using WebPageTest you might use for your testing. Additionally, you can run your own instance of WebPageTest locally. If you are interested in this approach, you might find Francis John’s blogpost useful.
In this article we have covered 7 of the most relevant aspects to take into account when using
preconnect resource hints. Working with these browser resource hints can be confusing and frustrating. I hope this article has provided a better understanding of CORS connections and why
dns-prefetch is used as a fallback for
preconnect. As always, remember to test your hypothesis to ensure your changes do not cause a performance degradation.
-  W3C — Ilya Grigorik— Resource Hints: https://www.w3.org/TR/resource-hints/#preconnect
-  W3C — Ilya Grigorik — Resource Hints: https://www.w3.org/TR/resource-hints/#dns-prefetch
-  Chromium — DNS Prefetching: https://www.chromium.org/developers/design-documents/dns-prefetching
-  MDN Web Docs — X-DNS-Prefetch-Control: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-DNS-Prefetch-Control
-  MDN Web Docs — Cross-Origin Resource Sharing (CORS): https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#what_requests_use_cors
-  Ilya Grigorik— Eliminating Roundtrips with Preconnect: https://www.igvita.com/2015/08/17/eliminating-roundtrips-with-preconnect/
-  Michael Crenshaw — Preconnect resource hint and the crossorigin attribute: https://crenshaw.dev/preconnect-resource-hint-crossorigin-attribute/
-  Milica Mihajlija— Google Developers —Establish network connections early to improve perceived page speed: https://web.dev/preconnect-and-dns-prefetch/#how-to-implement-relpreconnect
-  Google Developers —Preconnect to required origins: https://web.dev/uses-rel-preconnect/#improve-page-load-speed-with-preconnect
-  Ivan Akulov — PerfPerfPerf — Preload, prefetch and other <link> tags: https://3perf.com/blog/link-rels/#when-to-use-2
-  Andy Davies — Experimenting With Link Rel=preconnect Using Custom Script Injection in WebPageTest: https://andydavies.me/blog/2019/08/07/experimenting-with-link-rel-equals-preconnect-using-custom-script-injection-in-webpagetest/
-  Francis John—Local WebPagetest Using Docker: https://firstname.lastname@example.org/local-webpagetest-using-docker-90441d7c2513
Graphics in this post were constructed by the author. Screenshots taken from caniuse.com are under under the CC BY 4.0 license.