
What Is DNS Prefetching, and How Does It Speed Up Web Pages?
A modern web page rarely loads from a single domain. Fonts come from one host, analytics from another, images from a CDN, and a payment widget from a fourth. Every new hostname needs a DNS lookup before the browser can even start a connection, and on a slow mobile network each of those lookups can cost tens or hundreds of milliseconds. DNS prefetching is a small, low-risk technique that moves those lookups earlier so they are already finished when the browser needs them. If you want the broader picture of how resolver choice and TTLs influence load time, start with can DNS settings affect website speed; this article focuses specifically on what you can do in your HTML.
Below you will learn what DNS prefetching is, how browsers perform it, how it differs from preconnect, how to add it in plain HTML, HTTP headers, and React or Next.js, how to measure whether it helps, and when it is not worth bothering with.
What Is DNS Prefetching?
DNS prefetching is a browser optimization where hostnames are resolved to IP addresses in the background before any resource from them is requested. When the browser later fetches a script or font from that host, the answer is already in its DNS cache, so the lookup step takes effectively zero time.
You trigger it with a resource hint in the page's head:
<link rel="dns-prefetch" href="https://fonts.gstatic.com" />
The browser treats this as a hint, not a command. It may perform the lookup immediately, defer it, or skip it, but in practice every major browser honours dns-prefetch in some form.
Why the Lookup Step Matters
Before a browser can download anything from a new origin, it has to complete several sequential steps:
- DNS lookup — resolve
cdn.example.netto an IP address. - TCP handshake — open a connection (one round trip).
- TLS handshake — negotiate encryption (one more round trip with TLS 1.3).
- HTTP request — finally ask for the resource.
Each step waits for the previous one. If the browser only discovers a third-party hostname when it parses a script tag halfway down the page, or worse, when a script dynamically injects another script, the DNS lookup starts late and delays everything behind it. The cost is largest when:
- The user's resolver is far away or slow.
- The hostname is not in the resolver's cache because the domain is not popular.
- The user is on a high-latency mobile network.
- The resource is discovered late, for example a font referenced from inside a CSS file.
Prefetching does not make a lookup faster. It just runs it in parallel with other work, so it is no longer on the critical path.
How Browsers Prefetch DNS
Browsers prefetch DNS in two ways.
Explicit Hints
Any dns-prefetch or preconnect link in your HTML, or an equivalent Link HTTP header, tells the browser exactly which origins you expect to use. This is the reliable, controllable method and the one you should rely on.
Automatic Link Prefetching
Some browsers also scan anchor tags on a page and resolve the hostnames of links in the background, on the theory that the user might click one. This behaviour varies between browsers and is commonly disabled by default on HTTPS pages for privacy reasons, so do not depend on it. You can control it with a header or meta tag:
<meta http-equiv="x-dns-prefetch-control" content="off" />
Setting it to off tells supporting browsers not to resolve hostnames found in links on the page, which some privacy-focused sites prefer. Setting it to on opts into the behaviour where it is supported. In some browsers, off also suppresses explicit dns-prefetch hints on the page, so do not combine it with hints you rely on, and test in the browsers you care about.
dns-prefetch vs preconnect
The two hints are related but do different amounts of work:
| Hint | What the browser does | Cost if unused | Best for |
|---|---|---|---|
dns-prefetch | DNS lookup only | Almost none — one small query | Origins you might use, or many origins |
preconnect | DNS lookup, TCP handshake, and TLS handshake | An open socket that times out after a few seconds unused | The handful of origins you will definitely use early |
preconnect saves more time because it completes the connection too, but each one holds a connection open and uses CPU for the TLS handshake. Browsers close unused preconnected sockets after a short period, so preconnecting to an origin you only use 20 seconds later is wasted work. A common pattern is to preconnect to your two or three most critical third-party origins and dns-prefetch the rest.
You can also use both together for the same origin. Browsers that support preconnect will use it, and any that do not will still fall back to the DNS lookup:
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="dns-prefetch" href="https://fonts.gstatic.com" />
The crossorigin attribute matters for fonts and other resources fetched in CORS mode. Without it, the browser opens a non-CORS connection that cannot be reused for the font request, and a second connection is opened anyway.
How to Add DNS Prefetching to Your Site
In Plain HTML
Place hints as early as possible inside the head, before stylesheets and scripts:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="preconnect" href="https://cdn.example.net" />
<link rel="dns-prefetch" href="https://www.googletagmanager.com" />
<link rel="dns-prefetch" href="https://api.example-payments.com" />
<link rel="stylesheet" href="/styles/main.css" />
<title>Example</title>
</head>
</html>
Only use the scheme and host in href — paths are ignored for DNS purposes. Do not add a hint for your own origin; the browser already resolved it to load the page.
As an HTTP Link Header
Sending hints as a response header lets the browser act on them before it has parsed any HTML. With nginx:
location / {
add_header Link "<https://cdn.example.net>; rel=preconnect, <https://www.googletagmanager.com>; rel=dns-prefetch" always;
}
This adds a Link header to every response from that location. If your server or CDN supports 103 Early Hints, the same header can be sent in an interim response while the server is still generating the page, which gives the browser an even earlier head start.
In React and Next.js
React 19 added resource-hint functions to react-dom that work from any component, including server components. React deduplicates them and places them in the document head for you:
import { prefetchDNS, preconnect } from "react-dom";
export default function RootLayout({ children }) {
preconnect("https://cdn.example.net");
prefetchDNS("https://www.googletagmanager.com");
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
preconnect() and prefetchDNS() emit the corresponding link tags, so you do not need to hand-write them in a custom head. This works well in a Next.js App Router layout, where a hint declared once in the root layout applies to every page.
Adding Hints for Dynamically Discovered Hosts
If a user action is likely to need a new origin — opening a chat widget or a checkout modal — you can inject a hint on hover or focus, slightly before the click:
function hintOrigin(origin) {
if (document.querySelector(`link[rel="dns-prefetch"][href="${origin}"]`))
return;
const link = document.createElement("link");
link.rel = "dns-prefetch";
link.href = origin;
document.head.appendChild(link);
}
document.querySelector("#open-chat").addEventListener(
"pointerenter",
() => {
hintOrigin("https://chat.example-support.com");
},
{ once: true },
);
The first time the pointer enters the chat button, the browser resolves the chat provider's hostname, so by the time the widget script is requested the lookup is already done.
How to Measure Whether It Helps
Never add hints on faith; verify them. The Resource Timing API exposes DNS timing for every request:
performance.getEntriesByType("resource").forEach((entry) => {
const dns = entry.domainLookupEnd - entry.domainLookupStart;
if (dns > 0) {
console.log(`${new URL(entry.name).host}: ${dns.toFixed(1)} ms DNS`);
}
});
Paste this into the browser console after a page loads to list every resource whose DNS lookup took measurable time. After adding a prefetch hint for a host, its lookup should drop to zero or near zero on a cold load. One caveat: for cross-origin resources, detailed timings are reported as zero unless the third party sends a Timing-Allow-Origin header, so a zero is not always proof of success. Many large CDNs and analytics providers do send that header.
Other ways to check:
- DevTools Network panel. Hover over a request's waterfall bar to see the DNS Lookup segment. Disable the cache and test with network throttling to exaggerate the effect.
- WebPageTest or Lighthouse. Both show connection setup per request, and Lighthouse flags origins that would benefit from
preconnect. - A cold DNS cache. Your own machine has probably cached these hostnames already. Clear the browser's DNS cache between runs, or test in a fresh profile, to see the real first-visit cost.
Best Practices and Common Mistakes
- Prefetch what you actually use. Audit your page's third-party origins and hint the ones discovered late. Hinting origins the page never contacts wastes queries and leaks information.
- Keep preconnect to a minimum. Two to four preconnects is a reasonable ceiling. Too many compete with the main document for bandwidth and CPU early in the load.
- Put hints first. A hint placed after a blocking script only takes effect once that script finishes, which defeats the purpose.
- Match the crossorigin mode. Use
crossoriginonpreconnectfor fonts,fetch()calls, and module scripts; omit it for ordinary scripts, stylesheets, and images. - Remove hints when vendors change. A forgotten hint for an analytics provider you dropped two years ago is pure overhead.
- Consider privacy. Prefetching reveals to the user's resolver that a domain might be used, even if it never is. For privacy-sensitive pages, prefer fewer hints and leave automatic link prefetching off.
- Reduce origins instead where possible. Self-hosting fonts or proxying analytics through your own domain removes the lookup entirely, which beats any hint.
How It Fits with Other DNS Speed Improvements
DNS prefetching works inside the browser and only hides latency that already exists. It complements, rather than replaces, improvements on the DNS side: using a fast authoritative provider on anycast, choosing sensible TTLs so popular resolvers keep your records cached, and publishing HTTPS records so browsers can learn about HTTP/3 support in the same lookup. Encrypted DNS like DoH can add a little overhead to each query, which makes moving lookups off the critical path slightly more valuable, not less.
DNS Prefetching FAQ
All major current browsers support dns-prefetch in some form. Because it is only a hint, an unsupported browser simply ignores it, so it is always safe to include.
dns-prefetch only resolves the hostname. preconnect resolves the hostname and also opens the TCP connection and completes the TLS handshake. preconnect saves more time but costs more if the connection goes unused.
No. The browser already resolved your domain to load the page. Hints are only useful for other origins such as CDNs, font hosts, APIs, and third-party scripts.
There is no hard limit because each lookup is cheap, but keep the list to origins the page actually uses. A practical range is a handful of dns-prefetch hints and no more than about four preconnects.
Not directly. It can improve loading metrics such as Largest Contentful Paint when a critical resource comes from a third-party origin, and those metrics feed into page experience signals.
Yes, slightly. Prefetching tells the user's DNS resolver about domains the page might use, even if the user never triggers them. Some sites disable automatic link prefetching with the x-dns-prefetch-control header for this reason.
Either the hostname was already cached, the hint worked, or the third party does not send a Timing-Allow-Origin header, in which case the browser hides detailed timings. Test with a cold cache to tell them apart.
No. The crossorigin attribute only matters for preconnect, because it determines which kind of connection is opened. A DNS lookup is the same either way.
Conclusion
DNS prefetching is one of the cheapest performance wins available: a single line of HTML that removes a sequential network step from the moment the browser needs a third-party resource. It works best for origins discovered late in the page load, such as fonts referenced from CSS, scripts injected by tag managers, and widgets loaded on interaction. Pair it with a small number of preconnect hints for your most critical origins, place the hints at the very top of the head or send them as Link headers, and use the React or framework helpers if your site is built that way.
Then measure. Look at DNS timings in Resource Timing or DevTools with a cold cache, keep the hints that make a difference, and remove the rest. Fewer third-party origins is still the best optimization of all, but for the ones you cannot avoid, prefetching makes sure DNS is never the reason a page feels slow.
Here are some useful references for going deeper on DNS prefetching:
- MDN Web Docs: Using dns-prefetch — browser behaviour and usage guidance for the dns-prefetch hint.
- MDN Web Docs: rel=preconnect — how preconnect works and when to use the crossorigin attribute.
- MDN Web Docs: X-DNS-Prefetch-Control — controlling automatic hostname prefetching from links.
- W3C: Resource Hints — the specification that defined dns-prefetch, preconnect, and related hints.
- React Documentation: prefetchDNS — the React DOM API for emitting DNS prefetch hints from components.


