Preload vs Prefetch vs Preconnect

TL;DR / Preload fetches resources needed for the current page with high priority, prefetch fetches resources for future navigations at low priority, and preconnect establishes early connections without downloading anything.

How It Works

 ┌──────────┐          ┌──────────────┐
 │  <link>  │─────────→│  preconnect  │   DNS + TCP + TLS
 └──────────┘          └──────────────┘   (warm connection)
       │
       └──────────┐
                  │    ┌──────────────┐   current page
                  │───→│   preload    │   HIGH priority
                  │    └──────────────┘
                  │
                  │
                  │    ┌──────────────┐   future navigation
                  └───→│   prefetch   │   LOW priority, idle time
                       └──────────────┘

Edit diagram

Resource hints let you communicate loading intent to the browser. Without them, the browser discovers resources reactively: it parses HTML, finds a <link> to a stylesheet, downloads it, parses the CSS, finds a url() to a font, downloads that, and so on. Each step waits for the previous one. Resource hints let you short-circuit these dependency chains by telling the browser what it will need before it discovers it naturally.

Preconnect

<link rel="preconnect" href="https://api.example.com"> tells the browser to establish a connection to the specified origin immediately, without downloading any content. This means performing DNS resolution, the TCP handshake, and (for HTTPS) the TLS negotiation. On a typical connection, this saves 100-300ms when the first actual request to that origin is made.

Preconnect is most valuable for third-party origins that you know the page will contact: CDNs serving fonts, API servers, analytics endpoints, or image CDNs. The browser does not know about these origins until it encounters them in CSS, JavaScript, or subsequent HTML parsing. Preconnect gives it a head start.

<link rel="dns-prefetch" href="https://api.example.com"> is the lighter version: it only performs DNS resolution (typically 20-100ms), skipping TCP and TLS. It has broader browser support and lower overhead, making it suitable for origins where connection setup latency is less critical.

Preconnect has a timeout. If the connection is not used within roughly 10 seconds, the browser closes it. Preconnecting to origins that are not actually used wastes resources and contends for the browser's limited connection pool (typically 6 connections per origin for HTTP/1.1).

Preload

<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin> tells the browser to download this resource immediately with high priority, as part of the current page's critical rendering path. The as attribute is mandatory -- it tells the browser the resource type, which determines the fetch priority, request headers, and Content Security Policy checks.

Preload solves the discovery gap. A font referenced in CSS cannot be discovered until the CSS is downloaded and parsed. A JavaScript module imported dynamically cannot be known until the importing module executes. Preload moves the download to the start of the page load, running in parallel with other critical resources.

The as attribute must match how the resource is ultimately consumed. as="font" for fonts, as="script" for scripts, as="style" for stylesheets, as="image" for images, as="fetch" for API data. If as is wrong or missing, the browser may download it twice (once for the preload, once for the actual use).

crossorigin on font preloads is required even for same-origin fonts because the font-face spec requires CORS. Omitting it causes a double download since the CORS mode mismatch prevents reuse.

Prefetch

<link rel="prefetch" href="/next-page.js"> tells the browser to download this resource at the lowest priority during idle time, caching it for use on a future navigation. The resource is stored in the HTTP cache (not a special prefetch cache) and is available when the user navigates to a page that needs it.

Prefetch is a speculative optimization. The browser may choose not to honor it if the network is slow, battery is low, or data saver mode is enabled. It runs at the lowest priority, ensuring it never competes with resources needed for the current page.

The most effective prefetch targets are route bundles for likely-next navigations. If analytics show that 80% of users on page A navigate to page B, prefetching page B's JavaScript bundle means it loads instantly when they click. Similarly, prefetching above-the-fold images for the next page eliminates the visual loading gap.

Speculation Rules and Prerender

The deprecated <link rel="prerender"> has been replaced by the Speculation Rules API (<script type="speculationrules">), which uses a JSON configuration to declare prefetch and prerender intentions with URL pattern matching and eagerness levels (on hover, pointer-down, or eager). This gives browsers enough signal to prerender likely navigations without wasting resources on unlikely ones.

Ordering and Limits

Browsers process resource hints in document order but subject to global limits. Excessive preloads can hurt performance by consuming bandwidth meant for truly critical resources. The browser's preload scanner already discovers <img>, <script>, and <link> resources automatically. It cannot discover resources referenced in JavaScript (import(), fetch()) or CSS (url() in backgrounds, font-face). These hidden sub-resources are where preload provides the most value.

Gotchas

  • Preloaded resources not used within 3 seconds trigger a console warning -- Chrome warns "The resource was preloaded using link preload but not used within a few seconds." If you see this, the preload is wasteful or the as attribute is wrong, causing a double fetch.
  • Missing or incorrect as attribute causes double downloads -- the preloaded response is keyed by destination type. If preload says as="script" but the resource is used as a style, the browser fetches it again.
  • Prefetch is not guaranteed -- browsers may ignore prefetch hints on slow connections, low battery, or data saver mode. Never rely on prefetch for correctness; it is purely an optimization.
  • Preconnect without subsequent use wastes connections -- idle connections consume browser and server resources. Limit preconnect to origins you are certain the page will contact, typically 2-4 origins maximum.
  • crossorigin must match between preload and use -- a preload with crossorigin="anonymous" cannot be reused by a fetch with credentials: "include". The CORS mode mismatch causes a second download.