Oğuzhan KARAGÜZEL

OĞUZHAN KARAGÜZEL

Software Developer

Resource Hints: preload, prefetch, preconnect and dns-prefetch

Author: Oğuzhan KARAGÜZEL ·

When a browser loads a page, it reads the HTML from top to bottom and requests resources in the order it discovers them. The trouble is that the most important resources are often discovered late: the heading font is declared inside a CSS file, so the browser doesn't know it exists until the CSS has downloaded; a large hero image set as a CSS background or inserted by JavaScript is the last thing it notices. Resource hints are how you tell the browser "you'll need this soon, get ready now". In this article I explain the differences between dns-prefetch, preconnect, preload and prefetch, the fetchpriority attribute, font loading and the common mistakes.

The journey of a request: DNS, TCP and TLS

Before the browser can download a file from another domain (say, a font on a CDN), it has to connect to that server. That takes several steps before the file itself is even requested:

  1. DNS lookup: a domain such as cdn.example.com is resolved to the server's IP address.
  2. TCP handshake: the browser and the server exchange messages to open the connection (SYN, SYN-ACK, ACK).
  3. TLS handshake: for HTTPS, they agree on encryption keys. This takes one round trip with TLS 1.3 and two with TLS 1.2.
  4. Request: only then is the file itself requested.

Each step means at least one round trip (RTT) between browser and server. On desktop that is a few tens of milliseconds; on mobile connections a round trip of over 100 milliseconds is normal, and connecting to a new domain can take 300 milliseconds on its own. The first two resource hints move this waiting earlier.

dns-prefetch and preconnect: connecting early

dns-prefetch: resolve the address only

dns-prefetch asks the browser only to resolve the domain's IP address in advance. No connection is opened and nothing is downloaded:

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

It is a very cheap hint, well suited to third-party domains that may be used later on the page: a comment system, a map, or a video player loaded on user interaction. Since older browsers support it too, it works as a fallback alongside preconnect.

preconnect: set up the connection early

preconnect goes one step further and completes all three steps, DNS, TCP and TLS, in advance. When the file is requested, the connection is ready:

HTML
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="dns-prefetch" href="https://fonts.gstatic.com">

The crossorigin attribute here is not decoration. Files requested in CORS mode, such as fonts, use a separate connection pool. A connection opened without crossorigin can't be used for the font request, so the browser opens a second one and the hint is wasted.

Use preconnect sparingly. Every open connection costs the browser and the server work; a connection that isn't used within a few seconds is closed and that work is thrown away. The rule of thumb: preconnect only to the two or three most critical domains certain to be used in the first seconds of the page, dns-prefetch to the rest. Your own domain needs no hint; the connection the HTML arrived on is already open.

preload: download a critical file now

preload goes beyond the connection and starts downloading the file itself at high priority. It is for files this page will certainly use but the browser would discover late:

HTML
<!-- Heading font declared inside the CSS -->
<link rel="preload" href="/fonts/inter-700.woff2" as="font" type="font/woff2" crossorigin>

<!-- Hero image set as a CSS background -->
<link rel="preload" href="/img/hero.webp" as="image" fetchpriority="high">

Things to watch:

  • as is required. It tells the browser what kind of file this is, and priority is assigned accordingly. If as is missing or wrong, the file isn't recognised when it is used later and is downloaded a second time.
  • Font preloads need crossorigin, even when the font is on your own domain. Fonts are always requested in CORS mode; a font preloaded without crossorigin goes unused and is again downloaded twice.
  • An unused preload produces a warning. If a preloaded file isn't used within a few seconds of the page loading, the browser writes a warning to the console. It is a sign you have pulled something forward needlessly.
  • Preloading everything is preloading nothing. Preload takes priority away from other resources. If every file is high priority, the truly critical ones wait in line.

For responsive images, use imagesrcset and imagesizes so the preload picks the same candidate as the image tag. Otherwise the preload may fetch one size and the <img> another:

HTML
<link rel="preload" as="image"
      imagesrcset="/img/hero-640.webp 640w, /img/hero-1200.webp 1200w"
      imagesizes="(max-width: 768px) 100vw, 1200px">

prefetch: getting ready for the next page

prefetch is a hint not for this page but for the next one the user is likely to visit. The browser downloads the file at the lowest priority, while the network is idle, and keeps it in the cache:

HTML
<link rel="prefetch" href="/en/articles/next-article">

It suits the next step of a multi-step form, the next article in a series, or the dashboard that opens after the sign-in page. If the guess is wrong, the downloaded data is wasted, so with mobile data in mind, use it only for likely destinations. Chromium-based browsers also offer a more powerful version: with the Speculation Rules API, the next page can be not just downloaded but fully prepared in the background (prerendered).

The four hints compared

HintWhat it doesPriorityUse it for
dns-prefetchResolves the domain to an IP address onlyVery cheap, immediateThird-party domains that may be used later
preconnectCompletes the DNS, TCP and TLS stepsImmediate; use only a few2–3 critical domains certain to be used in the first seconds
preloadDownloads the fileHigh, for this pageLate-discovered critical files: fonts, hero images
prefetchDownloads the file and caches itLowest, when the network is idleFiles the next page will need

fetchpriority: stating priority directly

If the image is already in the HTML as an <img> tag, the browser discovers it early; the problem may be priority rather than discovery. Browsers request images at low priority at first and only learn which ones are above the fold once layout has been computed. fetchpriority gives them that information up front:

HTML
<!-- The page's largest image (LCP): high priority, no lazy loading -->
<img src="/img/hero.webp" width="1200" height="630" alt="..." fetchpriority="high">

<!-- Hidden slides of a carousel: low priority -->
<img src="/img/slide-2.webp" width="1200" height="630" alt="..." fetchpriority="low">

fetchpriority is supported in current versions of Chrome, Edge, Safari and Firefox; a browser that doesn't support it simply ignores it and nothing breaks. The practical distinction: if the image is in the HTML, fetchpriority="high" is enough; if CSS or JavaScript makes it late to discover, you need preload.

Fonts: preload and font-display: swap

Until a web font has downloaded, browsers may keep the text invisible for a while (FOIT, flash of invisible text). On a slow connection the user sees the page's skeleton but nothing to read. font-display: swap shows the text immediately in a fallback font and swaps it once the web font arrives:

CSS
@font-face {
    font-family: "Inter";
    src: url("/fonts/inter-var.woff2") format("woff2");
    font-weight: 100 900;
    font-display: swap;
}

With Google Fonts, the same setting is the &display=swap parameter at the end of the URL. swap has a cost: if the letter widths differ when the font switches, lines shift, and that shows up in CLS (Cumulative Layout Shift). Ways to reduce it:

  • Preload the critical font so the swap happens before the first render.
  • Bring the fallback font closer to the web font's metrics with size-adjust, ascent-override and similar descriptors.
  • If the look matters less than stability, use font-display: optional: if the font doesn't arrive quickly, it isn't swapped in at all for that page view.
  • Serve the font from your own server. The cost of connecting to a separate domain disappears entirely.

When preload and lazy loading collide

A common contradiction is preloading an image and marking the same image loading="lazy" in its <img> tag. Lazy loading tells the browser "this can wait until it gets close to the viewport"; preload says "download it now". The result is either a file downloaded early and left waiting, or the browser requesting it again without using the preload. Pick one:

  • If the image is visible on the first screen, it is critical: don't use loading="lazy", and add fetchpriority="high" or preload if needed. Lazy-loading the page's largest image (the LCP element) is the shortest way to a slower LCP.
  • If the image is further down, it isn't critical: use loading="lazy" and don't preload it.

Either way, don't forget the width and height attributes; the browser reserves the image's space in advance, and the page doesn't jump when it arrives.

Common mistakes and measuring

  • Preloading every CSS and JavaScript file. Files already at the top of the HTML don't need a preload; the browser discovers them early anyway.
  • Forgetting crossorigin on font preloads and as on image and font preloads. Both lead to the file being downloaded twice.
  • Adding preconnect to five or six different domains. Unused connections are closed and the work is wasted.
  • Lazy-loading the hero image, or making the same image both lazy and preloaded.
  • Prefetching large files for the next page. Every wrong guess spends the mobile user's data for nothing.

How to measure the effect

In the Network panel of Chrome DevTools, turn on the Priority column and look at the waterfall: do the critical font and hero image start right after the HTML, at high priority? Lighthouse and PageSpeed Insights also report a late-discovered LCP image, unused preconnects and invisible text. Measure again after every change; preload in particular can slow a page down instead of speeding it up when used in the wrong place.

Conclusion

Resource hints let you tell the browser in advance what it would otherwise learn later than you. dns-prefetch for domains that may be used later, preconnect for those certainly needed in the first seconds, preload for late-discovered critical files, prefetch for the next page. For an important image already in the HTML, fetchpriority="high" is often enough. The common rule is restraint: priority only works when few resources get it. For compression, caching, image optimisation and the other topics, see Website Speed and Core Web Vitals.

For further reading, I recommend MDN's pages on rel=preload, rel=preconnect and fetchpriority, and the articles on resource prioritisation at web.dev.