completefrontend
Open lab My progress
THE WEB PERFORMANCE FIELD GUIDE

DNS lookups

Every new hostname starts with a lookup. Follow a real one from the root servers down, see what caching and TTLs do, and hide lookups with hints.

By the end, you can
  • 01
    Follow a lookupSay where a browser finds a name's address, from its own cache to the root servers, and what each place costs.
  • 02
    Read a TTLExplain what a TTL decides, why a short one means more slow lookups, and why a long one slows down changes.
  • 03
    Hide and cut lookupsCount a page's hostnames, pick dns-prefetch, preconnect or neither for each, and say what an HTTPS record adds.

Every new hostname starts with a lookup

You typed completefrontend.com, or clicked a link to it, and this page appeared. It felt like one step. But your browser couldn't send a single byte to "completefrontend.com". The internet doesn't deliver to names. It delivers to numbers.

So before anything else, the browser had to find the number behind the name. That number is an IP address, like 199.36.158.100. Finding it is called a DNS lookup. DNS, the Domain Name System, is the internet's shared phone book: a list of names and the numbers they belong to.

Real-life analogyA name in your contacts

You want to call your friend Asha. You tap her name in your contacts, and the phone dials her number. You never type the number, and you may not even know it. But the call goes to the number, not to the name. If the phone couldn't find the number, there would be no call at all.

In real life: Asha's name in your contacts
In JavaScript: A hostname, like completefrontend.com
In real life: Her phone number
In JavaScript: The IP address, like 199.36.158.100
In real life: The phone finding the number before it dials
In JavaScript: The DNS lookup, before the browser can connect

A hostname is the name part of a web address: the part between https:// and the next slash. In https://fonts.gstatic.com/s/inter.woff2, the hostname is fonts.gstatic.com. Every hostname needs its own lookup.

Here is the answer the course got when it looked up this site's name:

The answer for this site's namePop out in the code editor (opens in a new tab)JavaScript
// The answer the course got for this site's name.const answer = { name: "completefrontend.com", address: "199.36.158.100", ttl: 3600 }; console.log(`${answer.name} lives at ${answer.address}.`);console.log(`Keep this answer for ${answer.ttl} seconds.`);
  • Line 1 is a comment. It says where the answer came from.
  • Line 2 holds the recorded answer: the name, its IP address, and a number called the TTL.
  • Line 4 prints completefrontend.com lives at 199.36.158.100.
  • Line 5 prints Keep this answer for 3600 seconds. That's one hour. Every answer says how long it stays good. The section on TTLs explains why that matters.

So how long did the lookup take? It depends on where the browser found the answer. Sometimes a lookup takes no time at all. Sometimes it takes more than a quarter of a second. Here is every place the browser can look, nearest first:

Six stops from left to right where a lookup can be answered: the browser's cache, the operating system's cache, the resolver's cache, then a root server, a .com server and the site's own name servers. In the recorded lookup of completefrontend.com, those last three took 126 ms, 53 ms, 80 ms.Browserits cacheSystemits cacheResolverits cacheRoot126 ms.com53 msSite80 msOn your deviceNetworkOnly on a miss259 ms in all
The browser stops at the first place that has the answer. Only when none of the first three has it does the walk from the root begin. The last three times are from the recorded walk.
  • On your device. The browser and the operating system each keep a short list of names they looked up recently. If the name is there, the lookup is free.
  • At your network's resolver. This is a server that looks names up for you. Asking it costs one round trip.
  • On the walk from the root. If the resolver doesn't know either, it asks other servers, one after another. Each one costs another round trip.
The main idea

A DNS lookup turns a hostname into an IP address. The browser can't connect to a host until it has one. The lookup is free when your device remembers the answer. It costs one round trip when the resolver remembers it, and several when nobody does. Every new hostname on a page risks that wait. So use fewer hostnames, and look the important ones up early.

Why does this matter? Latency vs bandwidth showed that round trips, not megabits, decide how fast most pages load. A lookup is one round trip, or several, before the first request to a host can even start. The system design lesson DNS, CDNs, and the edge follows a name to a nearby server. This lesson measures what each lookup costs a page.

How a lookup is resolved and cached

Here is one lookup of completefrontend.com, followed from the nearest place to the farthest.

First, the places that remember

The browser starts close to home. It checks its own cache: a short list of answers it kept from earlier lookups. Then it asks the operating system, which keeps a cache for every program on the device. Neither of these needs the network.

If neither has the name, the device asks a resolver. A resolver is a server whose job is to look names up for other computers. Your internet provider runs one. There are also public ones anyone can use, like Cloudflare's 1.1.1.1. The resolver keeps a big cache of its own, shared by everyone who uses it. If anyone asked for the name a little while ago, it answers at once.

Real-life analogyA receptionist with a notebook

You work in a big office, and you need a shop's phone number. First you check the sticky note on your own desk. If it isn't there, you ask the receptionist, who looks in her notebook. If it isn't in the notebook either, she phones around until she finds it, and writes it down for the next person who asks.

In real life: The sticky note on your own desk
In JavaScript: The browser's cache and the operating system's cache
In real life: The receptionist
In JavaScript: Your network's resolver
In real life: Her notebook
In JavaScript: The resolver's cache, shared by everyone who uses it
In real life: Phoning around
In JavaScript: The walk from the root servers down

Read this table from top to bottom. It's the order the browser tries, and each row costs more than the one above it:

Where an answer can come from
Where the browser looksWhat it remembersWhat it costs
The browser's own cacheThe names this browser looked up a short while ago.Nothing. The answer is already on your device.
The operating system's cacheThe names any program on this device looked up.Nothing. The answer is still on your device.
Your network's resolverThe names anyone using that resolver looked up.One round trip, from your device to the resolver and back.
The root, .com and the site's own name serversNothing yet. The resolver has to ask each one in turn.Several round trips, one after another.

When nobody knows: the walk from the root

What does the receptionist do when the number isn't in her notebook? She doesn't phone every office in the world. She reads the name from right to left.

Take completefrontend.com. Its last part, .com, is called the top-level domain. A resolver that has nothing cached asks three kinds of server, in this order:

  • A root server. It doesn't know any addresses. It only knows who looks after each top-level domain, like .com.
  • A .com server. It doesn't know the address either. It knows which servers look after completefrontend.com.
  • The site's own name server. This server holds the site's DNS entries, so it gives the real answer.

Each server is like an office that doesn't know the number, but knows which office does. A lookup that a cache can't answer is called a miss. The course did this walk itself, with a tool called dig, the way a resolver does on a miss. Here is what each server said:

Who said what on the walkPop out in the code editor (opens in a new tab)JavaScript
// The walk for this site's name, one server after another.const walk = [  { asked: "A root server", said: "ask the .com servers" },  { asked: "A .com server", said: "ask completefrontend.com's own name servers" },  { asked: "completefrontend.com's name server", said: "the address is 199.36.158.100" },]; for (const step of walk) console.log(`${step.asked} said: ${step.said}.`);
  • Lines 3 to 5 are the three recorded steps, in order. Each one says who was asked and what they said.
  • Line 8 prints one sentence for each step.
  • The first is A root server said: ask the .com servers.
  • The second is A .com server said: ask completefrontend.com's own name servers.
  • The third is completefrontend.com's name server said: the address is 199.36.158.100.

Only the last server knew the address. The first two only knew who to ask next. Each step was a round trip of its own, and the next one couldn't start until it came back. So the times add up:

How long the walk tookPop out in the code editor (opens in a new tab)JavaScript
// How long each server took to answer, in milliseconds (ms).const root = 126;const dotCom = 53;const siteServer = 80; const walkTime = root + dotCom + siteServer;console.log(`The whole walk took ${walkTime} ms.`);
  • Lines 2 to 4 hold how long each server took to answer, as the course recorded them.
  • Line 6 adds them up. Line 7 prints The whole walk took 259 ms.

That's more than a quarter of a second, and the browser hasn't started connecting yet. It's also about the longest the walk gets. A resolver keeps the root's and .com's answers in its cache too, so a miss can often skip the first steps. Google Public DNS says a resolver rarely has to ask more than two name servers.

The last step can't be skipped: only the site's own name server has the answer. Here, it took 80 ms. That's why the company that runs your name servers matters. The next section comes back to it.

Why the same name is fast one time and slow the next

So a lookup costs nothing, one round trip, or a walk. Which one do your visitors get? You might expect a big public resolver to always have your name. The course tested that. It asked 1.1.1.1 for completefrontend.com ten times in a row:

The same name, asked ten timesPop out in the code editor (opens in a new tab)JavaScript
// completefrontend.com, asked ten times in a row through the public resolver 1.1.1.1.const times = [24, 390, 36, 8, 107, 132, 12, 25, 21, 89]; console.log(`The fastest answer took ${Math.min(...times)} ms.`);console.log(`The slowest answer took ${Math.max(...times)} ms.`);
  • Line 2 holds the ten recorded times, in the order they came back.
  • Line 4 prints The fastest answer took 8 ms.
  • Line 5 prints The slowest answer took 390 ms.

Same name, same resolver, asked one after another. Yet the slowest answer took almost 50 times as long as the fastest. Even www.google.com, a name every resolver surely has, took anywhere from 9 to 110 ms.

Why? Google's Public DNS team explains it. A big resolver isn't one machine. It's many machines behind a load balancer, which hands each question to one of them at random. Each machine keeps its own cache. So the machine that answers you may not be the one that remembered your name.

Back in the office, it's like a front desk with ten receptionists, each with her own notebook. Whether you wait depends on who you happen to ask.

Google also measured what a miss costs. Its web crawler sees 130 ms on average to look up a name, when the servers answer. Some messages get lost on the way, and some servers never reply. Counting those, the real average is 300 to 400 ms. So you can't count on a lookup being cached, even for a popular name. Plan for the slow case.

Try it yourself. Pick where the answer is, and see how many trips over the network the lookup needs:

Playground: where's the answer?
Trips over the network4
1. your resolver15 ms
2. a root server126 ms
3. a .com server53 ms
4. the site's servers80 ms
Nowhere yetlookup
Lookup timeabout 274 ms
Try it yourself
The lookup

Nowhere yet: the resolver asks a root server, a .com server and the site's own name server, one after another. In the recorded walk, that was 126 ms + 53 ms + 80 ms. Add the trip to the resolver, and it's about 274 ms in all.

The times are the course's recorded lookups of completefrontend.com and www.google.com, through 1.1.1.1. On another network, they would be different.

TTLs and DNS providers

Back to the receptionist's notebook. How long should she trust a number she wrote down? If the shop moves, an old number is worse than none.

DNS answers this with a TTL, short for time to live. Every answer carries one. It says how many seconds any cache may keep the answer. After that, the cache must throw it away and ask again. This site's answer said 3600 seconds: one hour.

Real-life analogyA number with a 'check again' note

The receptionist writes each number in pencil, with a note: "check again after one hour". Until then, she gives it out without phoning anyone. After that hour, the next person who asks has to wait while she phones again. If the shop changes its number, she keeps giving out the old one until her note runs out.

In real life: "Check again after one hour"
In JavaScript: A TTL of 3600 seconds
In real life: Giving the number out without phoning
In JavaScript: A hit: the cache has the answer
In real life: The first person after the hour has to wait
In JavaScript: A miss: one lookup pays for the walk again
In real life: The old number, until the note runs out
In JavaScript: A new address takes up to one TTL to reach everyone

Here is the same thing over time, for one cache that's asked for the same name again and again:

A time line of one resolver answering the same name nine times. The first lookup is a miss and slow. The next four are hits, answered from the cache, until the TTL runs out. Then the next lookup is a miss again, and the TTL starts over, with three more hits.TTLmissmiss againhitshitstime
The first lookup is a miss and pays for the walk. Until the TTL runs out, the cache answers at once. The first lookup after that is a miss again.

Each tall bar is a miss: a slow lookup that pays for the walk. Each short bar is a hit: the cache had the answer, so it came back at once. A cache that's asked all the time misses once per TTL. So the TTL decides how often your name server gets asked:

How often a busy cache asks againPop out in the code editor (opens in a new tab)JavaScript
// This site's answer had a TTL of 3600 seconds: one hour.const ttl = 3600;const secondsInADay = 24 * 60 * 60; const asksPerDay = secondsInADay / ttl;console.log(`A busy cache asks again ${asksPerDay} times a day.`);
  • Line 2 is this site's TTL, in seconds.
  • Line 3 works out how many seconds there are in a day: 86,400.
  • Line 5 divides the day by the TTL. Line 6 prints A busy cache asks again 24 times a day.

Now make the TTL one minute:

The same sum with a one-minute TTLPop out in the code editor (opens in a new tab)JavaScript
// A TTL of 60 seconds: one minute.const ttl = 60;const secondsInADay = 24 * 60 * 60; const asksPerDay = secondsInADay / ttl;console.log(`A busy cache asks again ${asksPerDay} times a day.`);
  • Besides the comment, only line 2 changes: a TTL of 60 seconds.
  • Line 6 prints A busy cache asks again 1440 times a day.

That's 60 times as many misses. Each one is a visitor who waits for the walk. Google's DNS team notes that sites are choosing shorter TTLs, and that this means more lookups.

So why not always choose a long TTL? Remember the old number in the receptionist's notebook. If you move your site to a new address, caches keep sending visitors to the old one until their TTL runs out. Read this table across: each row is one choice, what you get, and what it costs.

Choosing a TTL
TTLWhat you getWhat it costs
Short: a minute to five minutesA new address reaches every visitor within minutes.More lookups miss the caches and pay for the whole walk.
Long: an hour to a dayMost lookups are answered from a cache.A new address can take that long to reach everyone.

Who answers on a miss: your DNS provider

On a miss, the last step of the walk is your site's own name server. The company that runs it is your DNS provider. Its speed is your visitors' wait. In the recorded walk, this site's provider answered in 80 ms. The farther the provider's servers are from the resolver, the longer that step takes. Google Public DNS lists "authoritative servers" that are "geographically remote" as a cause of slow misses.

Notice what you can and can't choose. You choose your DNS provider and your TTL. You don't choose your visitors' resolvers. They use their internet provider's, or a public one they picked.

dns-prefetch and preconnect

So far, every lookup happened at the moment the browser needed it. Could it happen earlier, before anything has to wait for it?

Picture the tea shop's page. Its heading uses a font from fonts.gstatic.com. But the browser finds that out late. First it reads the HTML. Then it downloads the stylesheet. Only then does it see the font, and start looking up fonts.gstatic.com. All that time, the heading waits for its font.

Real-life analogyLooking up a number while the kettle boils

You're making tea, and you know that afterwards you'll phone the tea supplier. So while the kettle boils, you look up their number. Or you go further: you dial, and someone picks up, so the line is ready the moment your tea is. But if you then say nothing for too long, they hang up.

In real life: The kettle boiling
In JavaScript: The browser downloading the HTML and the stylesheet
In real life: Looking up the number now
In JavaScript: dns-prefetch: only the lookup, early
In real life: Dialling, so the line is ready
In JavaScript: preconnect: the lookup, the connection and TLS, early
In real life: They hang up after a long silence
In JavaScript: The browser closes a connection that isn't used soon

A page can give the browser the same head start, with resource hints: lines in the page's head that tell the browser what it will need soon. They're hints, not orders. A busy browser may skip one.

dns-prefetch asks for the lookup only. Use it for hosts the page will need later:

Look up two hosts earlyHTML
<head>  <!-- Hosts the tea shop needs later: look up their addresses now. -->  <link rel="dns-prefetch" href="https://analytics.example.com">  <link rel="dns-prefetch" href="https://videos.example.com"></head>
  • Line 2 is a comment. The tea shop needs these two hosts later, not right away.
  • Lines 3 and 4 each ask the browser to look one host up now. When the page later loads a file from that host, the answer is already in the cache.

preconnect goes further. It does the lookup, then opens the connection and sets up TLS. TLS is the step that makes the connection private: the S in HTTPS. Use preconnect for one or two hosts the page needs right away, like the heading's font:

Get the font's line ready earlyHTML
<head>  <!-- The heading's font comes from here: get the whole line ready now. -->  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin></head>
  • Line 3 asks the browser to set up the whole connection to fonts.gstatic.com now.
  • It ends with crossorigin. Browsers fetch fonts in a special mode, called anonymous mode. A preconnect only helps if it's made in the same mode, so a font host needs crossorigin. Without it, the browser only does the lookup.

Here is what each hint moves. Each row loads the font, measured in round trips:

Three rows, each loading a font from another host after the HTML and its stylesheet, measured in round trips. With no hint, the DNS lookup, TCP connection, TLS handshake and font request all come after the stylesheet: four round trips. With dns-prefetch, the lookup happens while the stylesheet loads: three after it. With preconnect, the lookup, connection and handshake happen early: only the font request is left, one round trip.No hintHTMLCSSDNSTCPTLSfontdns-prefetchHTMLCSSDNSTCPTLSfontpreconnectHTMLCSSDNSTCPTLSfontthe stylesheet names the font
A hint moves work into time the page spends waiting anyway. dns-prefetch moves the lookup. preconnect moves the whole connection.

With no hint, the lookup, the connection, TLS and the font request all wait until the stylesheet names the font. With dns-prefetch, the lookup is already done. With preconnect, only the font request is left. The work doesn't disappear. It moves into time the page was spending waiting anyway.

Read this table across to choose a hint. Each row says what the hint does early, and what it's for:

Which hint, when
HintWhat it does earlyUse it for
dns-prefetchIt looks up the address.Hosts the page needs later, or many hosts.
preconnectIt looks up the address, opens the connection and sets up TLS.One or two hosts the page needs right away.
NeitherNothing.Your own host, which is already looked up, and hosts most visits never use.

A few rules keep hints from backfiring. They come from web.dev and MDN:

  • Preconnect to one or two hosts, not ten. web.dev says the browser closes any connection that isn't used within 10 seconds. Extra connections can also delay more important files.
  • Don't hint your own hostname. MDN explains why: the browser looked it up before it could read the hint.
  • Give each hint its own tag. Writing rel="preconnect dns-prefetch" in one <link> cancels the preconnect in Safari.
  • A hint can come from the server too. Both work as an HTTP Link header, which the browser can act on before it reads any HTML.

Browsers also guess some lookups on their own. Ilya Grigorik describes them looking up likely hostnames from your browsing history, or when you hover over a link. A hint is for the hosts the browser can't guess. Resource hints covers these two hints and three more.

HTTPS DNS records for faster HTTP/3

A lookup usually brings back one thing: an address. Could it tell the browser more?

Here's why that would help. HTTP/3 is the newest version of HTTP, the language browsers and servers speak. It runs over a newer kind of connection, called QUIC. HTTP/3 and QUIC shows what it gains. But a browser can only use HTTP/3 if it knows the server speaks it.

How does it find out? Usually the server says so in a response header, Alt-Svc. Cloudflare's engineers point out the catch: that header only arrives after the browser has already connected. So the first connection uses an older version. Only later connections can use HTTP/3.

Real-life analogyA phone book that prints the direct line

You call a shop's main number for the first time, and it takes a while to get through. At the end, they tell you: "Next time, use our direct line. It's quicker." If the phone book had printed the direct line next to the main number, even your first call would have been quick.

In real life: Calling the main number
In JavaScript: The first connection, the old way, with HTTP/2
In real life: "Next time, use our direct line"
In JavaScript: The Alt-Svc header in the server's reply
In real life: The phone book printing the direct line
In JavaScript: An HTTPS record that lists h3
In real life: A quick first call
In JavaScript: HTTP/3 from the very first connection

An HTTPS record is that phone book entry. It tells the browser how to connect, before it connects. RFC 9460 made it a standard in 2023. A record is one entry in DNS: a name, a type, and its data. The address you met earlier came from an A record, the type that holds an address.

Here is the HTTPS record the course looked up for cloudflare.com, cut to one address of each kind:

Read the protocols from an HTTPS recordPop out in the code editor (opens in a new tab)JavaScript
// cloudflare.com's HTTPS record, with one address of each kind.const record = "1 . alpn=h3,h2 ipv4hint=104.16.132.229 ipv6hint=2606:4700::6810:84e5";const names = { h3: "HTTP/3", h2: "HTTP/2" }; const alpn = record.split(" ").find((part) => part.startsWith("alpn="));const protocols = alpn.replace("alpn=", "").split(",");console.log(`It speaks ${protocols.map((protocol) => names[protocol]).join(" and ")}.`);

Line 2 is the record. Read it from left to right:

  • 1 is its priority. A name can have several records, and the browser tries the lowest number first.
  • . means "this same name": connect to cloudflare.com itself.
  • alpn=h3,h2 lists the protocols the site speaks. h3 is HTTP/3, and h2 is HTTP/2.
  • ipv4hint and ipv6hint are addresses. The browser can start connecting without waiting for a separate address lookup.

The rest of the code reads the protocols:

  • Line 3 gives each protocol code a readable name.
  • Line 5 finds the part that starts with alpn=. Line 6 splits it into h3 and h2.
  • Line 7 prints It speaks HTTP/3 and HTTP/2.
Two rows, each a first visit to a site that speaks HTTP/3. With no HTTPS record, the lookup brings back only an address. The browser opens a TCP and TLS connection and sends an HTTP/2 request. The reply tells it that HTTP/3 is there for next time. With an HTTPS record, the lookup brings back an address and h3. The browser opens a QUIC connection and sends an HTTP/3 request straight away.No HTTPS recordlook upTCP + TLSHTTP/2 requestan addressthe reply says: HTTP/3 next timeHTTPS recordlook upQUIC + TLSHTTP/3 requestan address + h3
Without the record, the browser hears about HTTP/3 only in the reply. With it, the lookup itself says h3, so even the first connection can use HTTP/3.

completefrontend.com had no HTTPS record when the course looked. So browsers learn this site's protocols the older way, from the reply.

For a site that speaks HTTP/3, an HTTPS record lets even a visitor's first connection use it. You don't always have to write one by hand. Cloudflare, for example, publishes one by itself for the sites it serves, with HTTP/3 in it when a site turns HTTP/3 on.

Counting hostnames on a page

So far, one hostname. But a page rarely uses just one. Its fonts, scripts, pictures and ads often come from other hosts. Each new hostname is one more lookup, and usually one more connection too.

It's like a shopping list. Five things from one shop is one trip. Five things from five shops is five trips, even if each trip is short.

How many hosts does a real page use? The Web Almanac 2024 counted third parties: companies other than the site itself, such as analytics, ads or fonts. Line up the top million sites, and the page in the middle used 27 of them. Among the top thousand sites, the page in the middle used 66. Each third party brings at least one hostname of its own.

To count a page's hostnames, take each file's address and keep its hostname. JavaScript's URL does that for you:

Find a file's hostnamePop out in the code editor (opens in a new tab)JavaScript
const fontFile = new URL("https://fonts.gstatic.com/s/inter/v13/inter.woff2"); console.log(`This file comes from ${fontFile.hostname}.`);
  • Line 1 turns the font file's address into a URL object, which splits the address into parts.
  • Line 3 prints its hostname part: This file comes from fonts.gstatic.com.

Now do it for every file on the tea shop's page:

Count the tea shop's hostnamesPop out in the code editor (opens in a new tab)JavaScript
// Every file the tea shop's page loads.const files = [  "https://shop.example.com/",  "https://cdn.example.net/app.js",  "https://fonts.googleapis.com/css2?family=Inter",  "https://fonts.gstatic.com/s/inter/v13/inter.woff2",  "https://cdn.example.net/hero.jpg",  "https://www.googletagmanager.com/gtag/js",]; const hosts = new Set(files.map((file) => new URL(file).hostname));console.log(`${files.length} files come from ${hosts.size} hostnames.`);
  • Lines 3 to 8 are the page's six files.
  • Line 11 turns each file into its hostname and puts them in a Set. A Set keeps each value only once, so two files from cdn.example.net count as one host.
  • Line 12 prints 6 files come from 5 hostnames.

Notice that Google Fonts alone needs two hosts. The stylesheet comes from fonts.googleapis.com, and the font files from fonts.gstatic.com. That's two lookups and two connections for one font.

Now count this page's own hostnames, in your own browser:

Playground: this page's hostnames
What Count does, shortenedJavaScript
const hosts = new Map();for (const file of performance.getEntriesByType("resource")) {  const host = new URL(file.name).hostname;  hosts.set(host, [...(hosts.get(host) ?? []), file]);} for (const [host, files] of hosts) {  const dns = files[0].domainLookupEnd - files[0].domainLookupStart;  show(host, files.length, dns);}
Hostsnot counted yet

Nothing counted yet.

Try it yourself
Hostnames on this page

Press Count. The panel groups every file this page has loaded so far by its hostname. Where your browser lets it, it also reads each host's lookup time.

Read from your own browser with Resource Timing. Another site's lookup shows only if that site sends a Timing-Allow-Origin header. Otherwise the browser hides it.

In the panel's code:

  • Line 3 finds each file's hostname. Line 4 adds the file to that host's group.
  • Line 8 reads the lookup time of the host's first file. Later files from the same host reuse its answer.

Some hosts may say "hidden". The browser only shares another site's timing if that site allows it, with a Timing-Allow-Origin header.

Fewer hostnames mean fewer lookups and fewer connections. Serve what you can from your own host. For each new third party, ask whether it's worth the trip.

Which hint, if any?

Here are seven hosts a page might use. Each needs a different hint, or none. For each one, ask two questions. Does the page need it right away? And will most visits use it at all?

Which hint, if any?
  • The font host your main heading needs right away
  • The image CDN that serves your LCP picture
  • An analytics host your script calls after the page loads
  • Six hosts for embeds further down the page
  • Your own site's hostname
  • A chat widget's host, loaded only if someone clicks Help
  • A host the HTML already loads a stylesheet from, at the top of the head
Try it yourself
0 of 7 correct

Sort each host by the hint it should get.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Common mix-ups

  • "DNS is only slow on bad networks." No. A miss walks from server to server on any network. The recorded walk took 259 ms.
  • "Once a name is looked up, it's cached for everyone." No. A big resolver is many machines, each with its own cache. And every answer expires when its TTL runs out.
  • "Preconnect to every third party." No. The browser closes a connection it doesn't use within 10 seconds, and extra connections can delay important files.
  • "A preconnect to a font host works without crossorigin." No. Fonts are fetched in anonymous mode. Without crossorigin, the browser only does the lookup.
  • "dns-prefetch for my own hostname speeds it up." No. The browser looked it up before it could read the hint.
  • "A longer TTL is always better." No. You get fewer misses, but a new address takes that long to reach everyone.
  • "Browsers always use HTTP/3 when a site supports it." Not on the first connection, unless an HTTPS record says so.

Practice exercises

Answer first, then use the hints.

Exercise 1 · Warm-upA 30-second TTL

This answer's TTL is 30 seconds. What does the code print?

Starter codePop out in the code editor (opens in a new tab)JavaScript
// A TTL of 30 seconds.
const ttl = 30;
const secondsInADay = 24 * 60 * 60;

const asksPerDay = secondsInADay / ttl;
console.log(`A busy cache asks again ${asksPerDay} times a day.`);

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upTwo more files

    Two files are added to the tea shop's page. How many hostnames does it print now?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    // Every file the tea shop's page loads.
    const files = [
      "https://shop.example.com/",
      "https://cdn.example.net/app.js",
      "https://fonts.googleapis.com/css2?family=Inter",
      "https://fonts.gstatic.com/s/inter/v13/inter.woff2",
      "https://cdn.example.net/hero.jpg",
      "https://www.googletagmanager.com/gtag/js",
      "https://cdn.example.net/logo.svg",
      "https://player.example.org/embed.js",
    ];
    
    const hosts = new Set(files.map((file) => new URL(file).hostname));
    console.log(`${files.length} files come from ${hosts.size} hostnames.`);

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeA site without HTTP/3

      This record belongs to a site that doesn't speak HTTP/3. What does the code print?

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      // The HTTPS record of a site that only speaks HTTP/2.
      const record = "1 . alpn=h2 ipv4hint=192.0.2.10";
      const names = { h3: "HTTP/3", h2: "HTTP/2" };
      
      const alpn = record.split(" ").find((part) => part.startsWith("alpn="));
      const protocols = alpn.replace("alpn=", "").split(",");
      console.log(`It speaks ${protocols.map((protocol) => names[protocol]).join(" and ")}.`);

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeWhen nobody has the answer

        In the where's-the-answer playground, pick Nowhere yet. About how long does the whole lookup take, in ms?

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeMoving day

          Your site's answer has a TTL of 86,400 seconds. You move to a new server and change the address. For how long, at most, might a cache keep sending visitors to the old one?

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 6 · ChallengeThe step after the lookup, in DevTools

            Open DevTools' Network panel on a page that loads files from another host, and reload. Click a request to that host and open its Timing tab. Which phase comes right after the DNS lookup?

            Answer, then press Check. Spacing and letter case don’t matter.

              Check your understanding

              For each question, ask where the answer comes from, and whether the lookup is still in the way.

              The DNS lookups quiz · 7 questionsScore: first tries count
              1. Question 1 of 7What does the walk-time example print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                // How long each server took to answer, in milliseconds (ms).
                const root = 126;
                const dotCom = 53;
                const siteServer = 80;
                
                const walkTime = root + dotCom + siteServer;
                console.log(`The whole walk took ${walkTime} ms.`);

                Choose an answer to see the explanation.

              2. Question 2 of 7What does a DNS answer's TTL decide?

                Choose an answer to see the explanation.

              3. Question 3 of 7What does this five-minute TTL example print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                // A TTL of 300 seconds: five minutes.
                const ttl = 300;
                const secondsInADay = 24 * 60 * 60;
                
                const asksPerDay = secondsInADay / ttl;
                console.log(`A busy cache asks again ${asksPerDay} times a day.`);

                Choose an answer to see the explanation.

              4. Question 4 of 7Why does a dns-prefetch hint for your own hostname do nothing?

                Choose an answer to see the explanation.

              5. Question 5 of 7When is preconnect a better choice than dns-prefetch?

                Choose an answer to see the explanation.

              6. Question 6 of 7What can an HTTPS record tell a browser before it connects?

                Choose an answer to see the explanation.

              7. Question 7 of 7In the recorded lookups, why did the same name come back fast one time and slow the next?

                Choose an answer to see the explanation.

              Key takeaways

              • Every new hostname needs a lookup before the browser can connect to it.
              • The browser looks nearest first: its own cache, the system's, then the resolver's. Only on a miss does the walk from the root begin.
              • The recorded walk took 259 ms. Google measures 300 to 400 ms on average for a miss, once failures are counted.
              • A big resolver is many machines with separate caches, so the same name can be fast one time and slow the next.
              • The TTL decides how long caches keep an answer. Short means more misses. Long means slower changes.
              • Preconnect one or two critical hosts, dns-prefetch the rest, and never hint your own.
              • An HTTPS record lets even the first connection use HTTP/3.
              • Count your page's hostnames, and cut the ones it doesn't need.

              Remember the one-liner.
              Every hostname is a question: ask fewer, and ask early.

              Sources

              The facts on this page come from these docs, standards and data:

              Coming next: TCP connections and slow start. After the lookup comes the connection. A new TCP connection starts by sending a little, then more and more. That's why the first 14 KB of a page matter.

              CompleteFrontend Clear concepts. Working examples.