Back to Performance
Performance
medium
mid

What is the difference between preload, prefetch, preconnect, and dns prefetch?

preload = high-priority current-page resource. prefetch = low-priority future-navigation resource. preconnect = warm up TCP+TLS to an origin. dns-prefetch = resolve DNS only. Use the right one or you waste bandwidth.

6 min read·~10 min to think through

Resource hints tell the browser to do work earlier than it otherwise would. Each has a precise purpose; mixing them up wastes bandwidth or slows the page.

<link rel="preload"> — fetch this NOW for the current page.

html
<link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin>

High priority. The browser fetches the resource immediately, in parallel with HTML parsing, but doesn't use it until the parser hits the actual reference. Saves a round-trip on resources discovered late (CSS-referenced fonts, JS-loaded chunks).

The as attribute is required — it sets priority and lets the browser apply the right Accept header / CORS rules. Without it, the resource may be downloaded twice.

<link rel="modulepreload"> — same as preload, but for JS modules. Resolves and parses dependency graphs ahead of time. Use for ES modules; as="script" works too but modulepreload is more correct.

<link rel="prefetch"> — fetch this for a LIKELY next navigation.

html
<link rel="prefetch" href="/dashboard.js">

Low priority. Browser fetches when idle. Cached for use on the next page. Used by Next.js (<Link prefetch> defaults to true) and Nuxt to make hover-or-near-visible links instant. Bandwidth-aware — Chrome respects "data saver" mode.

<link rel="preconnect"> — open TCP + TLS to an origin NOW.

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

Saves DNS lookup + TCP handshake + TLS negotiation (typically 100–300ms). Apply to origins you'll definitely hit (CDN, image host, analytics). Limit to ~4 — each preconnect costs a TCP connection.

The crossorigin attribute is needed for fonts and other CORS-required resources; otherwise the browser opens an anonymous connection that won't be reused for the credentialed request.

<link rel="dns-prefetch"> — resolve DNS only.

html
<link rel="dns-prefetch" href="https://maybe-needed.com">

Cheaper than preconnect (no TCP/TLS). Right for "we might hit this origin." Often used as a fallback for browsers that don't support preconnect.

<link rel="prerender"> — historical; deprecated in most browsers. Replaced by the Speculation Rules API (Chromium):

html
<script type="speculationrules">
{ "prerender": [{ "where": { "selector_matches": "a[href^='/products/']" }, "eagerness": "moderate" }] }
</script>

Renders the next page in a hidden tab so navigation is instant. Powerful but risky — analytics fire, side effects run. Use only for safe GET pages.

<link rel="preload" as="image" imagesrcset> — for responsive image hero (LCP optimization):

html
<link rel="preload" as="image" imagesrcset="/hero-mobile.webp 600w, /hero-desktop.webp 1600w" imagesizes="100vw">

Tells the browser to start the LCP image fetch before the parser sees the <img>.

Decision tree.

  • Need this resource for the current page, browser doesn't see it early? → preload.
  • Likely next page resource? → prefetch.
  • Will hit a third-party origin soon? → preconnect (or dns-prefetch if uncertain).
  • Want to fully render the next page in advance? → Speculation Rules API.

Common mistakes.

  • Preloading everything → priority inversion, browser starves visible resources to fetch hints.
  • Forgetting as on preload → resource fetched twice.
  • Forgetting crossorigin on font preload → fetched twice (anonymous + credentialed).
  • Preloading large JS chunks the user never needs → wasted bandwidth, hurts metered users.

Measure. DevTools Network panel shows the priority and timing. Lighthouse flags wasted preloads ("Preload requests are not used within a few seconds").

Code

html
<!-- LCP image -->
<link rel="preload" as="image" href="/hero.webp" fetchpriority="high">

<!-- Web font -->
<link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin>

<!-- Third-party origin -->
<link rel="preconnect" href="https://api.example.com" crossorigin>

<!-- Speculative future page (low priority) -->
<link rel="prefetch" href="/checkout.js">
Common preload patterns

Follow-up questions

  • Why does font preload need crossorigin?
  • When would you choose dns-prefetch over preconnect?
  • How does Next.js Link's prefetch behavior work?
  • What's the Speculation Rules API replacing?

Common mistakes

  • Omitting `as` on preload — resource fetched twice.
  • Preloading too much — priority inversion, browser starves real work.
  • Using prefetch for current-page resources — too low priority.
  • Preconnect to many origins — each costs a TCP connection.

Performance considerations

  • preload + fetchpriority='high' is the strongest signal for LCP images.
  • preconnect saves up to 300ms per third-party origin on slow networks.
  • Lighthouse 'wasted preload' audit catches misuses.

Edge cases

  • Mobile browsers may ignore prefetch under 'data saver' mode.
  • Cross-origin font preload without crossorigin → cache miss when the actual font request comes in.
  • Speculation Rules API has UA support; degrade gracefully.

Real-world examples

  • Next.js prefetches linked routes on hover/visibility. Stripe Elements preconnects to api.stripe.com on page load.

Senior engineer discussion

Senior signal: knowing the precise role of each hint, when over-using preload backfires, and the modern Speculation Rules replacement for prerender.

Related questions