Skip to content

DNS Resolution

intermediate14 min read

You Type a URL and Wait. What Just Happened?

You type stripe.com into your browser and hit Enter. Before a single byte of HTML arrives, before any TCP connection opens, before TLS negotiates encryption, something else happens first. Something invisible. Something that can add 20-200ms to your page load before the "real" work even begins.

DNS resolution. The internet's phone book lookup.

Every developer knows "DNS translates domain names to IP addresses." But most stop there. They don't know that a single DNS lookup can involve 4+ separate servers across the globe. They don't know there are 5 caching layers that can short-circuit the process. And they definitely don't know that a misconfigured TTL or a missing dns-prefetch hint can silently add hundreds of milliseconds to every navigation on their site.

Let's fix that.

The Mental Model

Mental Model

DNS works like calling 411 for a phone number. You ask your local operator (recursive resolver) for "stripe.com." The local operator doesn't know every number in the world, so they call the national directory (root nameserver) who says "try the .com department" (TLD nameserver). The .com department says "Stripe's direct line is handled by this specific office" (authoritative nameserver). That office gives the actual phone number (IP address). Your local operator writes it down (caches it) so next time anyone asks, the answer is instant.

The Four Players in Every DNS Lookup

When your browser needs to resolve a domain, up to four types of DNS servers get involved:

Here's the thing most people miss: the recursive resolver is not just a relay. It's the workhorse. It takes your single query and potentially makes multiple queries to root, TLD, and authoritative servers to assemble the answer. That's why it's called "recursive" — it recursively resolves the chain on your behalf.

Step-by-Step: Resolving stripe.com

Execution Trace
Browser cache check
Browser checks its internal DNS cache
Cached entries from recent lookups (usually 60s)
OS cache check
OS resolver cache (systemd-resolved, mDNSResponder)
Shared across all apps on the machine
Router cache check
Home router may cache DNS responses
Often overlooked caching layer
Recursive resolver
Query sent to configured DNS server (ISP or 8.8.8.8)
This server does the heavy lifting
Root nameserver
Resolver asks root: 'Where is .com?'
Root responds with TLD server addresses
TLD nameserver
Resolver asks .com TLD: 'Where is stripe.com?'
TLD responds with authoritative NS addresses
Authoritative nameserver
Resolver asks Stripe's NS: 'What is the IP for stripe.com?'
Returns A record: 99.86.x.x (or similar)
Response cached
Resolver caches the answer, returns IP to browser
Cached for the duration specified by TTL

In practice, steps 5-7 are often skipped because the recursive resolver already has cached answers for popular domains. When you visit stripe.com, your ISP's resolver almost certainly has the answer cached because thousands of other users have asked recently.

Quiz
Why is it called a 'recursive' resolver?

The Five Caching Layers

DNS resolution would be painfully slow if every lookup hit the full chain. In reality, there are five caching layers that short-circuit the process:

  1. Browser DNS cache — Chrome, Firefox, and Safari all maintain an internal DNS cache. Chrome's lives at chrome://net-internals/#dns. Typical lifetime: 60 seconds (browser-specific, ignores TTL in some cases).

  2. OS DNS cache — Your operating system maintains a system-wide cache. On macOS it's mDNSResponder, on Linux it's often systemd-resolved. Shared across all applications.

  3. Router cache — Your home/office router often caches DNS responses. This benefits all devices on the network.

  4. ISP recursive resolver cache — Your ISP's DNS server caches responses based on TTL. Popular domains like google.com are almost always cached here.

  5. TLD and root hints — Recursive resolvers cache the locations of TLD servers, so they rarely need to ask the root servers at all.

Common Trap

Chrome's DNS cache does NOT always respect TTL values. Chrome may cache DNS entries for up to 60 seconds regardless of what the authoritative server specified. This means setting a 10-second TTL on your DNS records won't guarantee 10-second freshness in Chrome. If you're doing a DNS migration, factor in browser-specific caching behavior on top of your TTL.

Quiz
You change a DNS record and set TTL to 30 seconds. A user still sees the old IP 2 minutes later. What is the most likely reason?

TTL: The Knob You're Probably Ignoring

TTL (Time to Live) tells resolvers how long to cache a DNS response, measured in seconds. It's set on each DNS record by the domain owner:

stripe.com.    300    IN    A    99.86.4.8

That 300 means "cache this answer for 300 seconds (5 minutes)."

The TTL Tradeoff

Low TTL (30-300s)High TTL (3600-86400s)
Fast failover and migrationsFewer DNS lookups per user
More DNS queries hitting your nameserversBetter perceived performance
Higher resolver loadSlower to propagate changes

Production tip: Before a DNS migration, lower your TTL to 60-300 seconds days in advance. Old high TTLs need to expire from caches first. If your TTL is 86400 (24 hours), you need to lower it at least 24 hours before the migration so all caches have the new, shorter TTL when you flip the record.

DNS Record Types That Matter

You'll encounter these frequently as a frontend engineer:

RecordPurposeExample
AMaps domain to IPv4 addressstripe.com → 99.86.4.8
AAAAMaps domain to IPv6 addressstripe.com → 2600:1f18:...
CNAMEAlias one domain to anotherwww.stripe.com → stripe.com
NSDelegates a domain to nameserversstripe.com NS ns1.p44.dynect.net
MXMail server routingNot relevant for web perf
TXTArbitrary text (SPF, DKIM, verification)Domain ownership verification
CNAME chains are performance traps

A CNAME doesn't resolve directly to an IP. It points to another domain, which then needs its own DNS lookup. If that domain is also a CNAME, you get a chain. Each hop adds latency. CDN providers like Cloudflare use CNAME flattening to return the IP directly, avoiding the extra lookups.

DNS-over-HTTPS (DoH)

Traditional DNS queries are sent in plaintext UDP. Anyone between you and the resolver (your ISP, your coffee shop's WiFi, a compromised router) can see every domain you visit and potentially tamper with responses.

DNS-over-HTTPS encrypts DNS queries inside HTTPS connections:

  • Privacy: Your ISP can't see which domains you're resolving
  • Integrity: Responses can't be tampered with in transit
  • Performance tradeoff: The initial connection setup is slower (TLS handshake), but subsequent queries reuse the connection via HTTP/2 multiplexing
  • Browser support: Chrome, Firefox, Edge, and Safari all support DoH. Firefox enables it by default in the US.

The resolver still sees your queries (you're trusting Cloudflare's 1.1.1.1 or Google's 8.8.8.8 instead of your ISP), but the path between you and the resolver is encrypted.

Quiz
What problem does DNS-over-HTTPS solve?

DNS and Web Performance

Here's where this matters for your day job. DNS resolution happens before anything else — before TCP, before TLS, before the first byte of HTML. On a cold load with no cache hits, DNS can take 20-200ms. On mobile networks with high latency resolvers, it can exceed 500ms.

DNS Prefetching

You can tell the browser to resolve domains early using the dns-prefetch resource hint:

<link rel="dns-prefetch" href="https://fonts.googleapis.com">
<link rel="dns-prefetch" href="https://cdn.stripe.com">
<link rel="dns-prefetch" href="https://api.analytics.io">

This triggers DNS resolution in the background while the browser parses the HTML. By the time it encounters a resource from that domain, the IP is already cached. Zero-cost, zero-risk optimization.

Preconnect: DNS + TCP + TLS in One Shot

For critical third-party origins, preconnect goes further — it completes DNS, TCP, and TLS before the resource is needed:

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

Use preconnect for origins you know you'll need immediately (fonts, critical APIs). Use dns-prefetch for origins that might be needed (analytics, secondary CDNs). Don't preconnect to everything — each connection consumes resources.

Don't over-prefetch

Every preconnect opens a TCP connection and does a TLS handshake. If you preconnect to 15 origins, you're burning CPU and memory on connections you might not use. Limit preconnect to 2-4 critical origins. Use dns-prefetch for the rest.

Reducing DNS Lookups

Each unique origin in your page requires a separate DNS lookup (on first visit):

<!-- 6 unique origins = 6 DNS lookups -->
<link href="https://fonts.googleapis.com/css2?..." rel="stylesheet">
<script src="https://cdn.segment.com/analytics.js"></script>
<script src="https://js.stripe.com/v3"></script>
<img src="https://images.unsplash.com/..." alt="...">
<script src="https://www.googletagmanager.com/gtag/js"></script>
<iframe src="https://www.youtube.com/embed/..."></iframe>

Each lookup can add 20-120ms on the first visit. Strategies to minimize the impact:

  • Consolidate origins — self-host fonts and analytics where possible
  • Use dns-prefetch — for all third-party origins you can't eliminate
  • Use preconnect — for the 2-3 most critical third-party origins
  • Fewer third-party scripts — the best DNS lookup is the one you don't make
How browsers handle DNS failures

If a DNS lookup fails (NXDOMAIN, timeout, SERVFAIL), browsers have different retry behaviors. Chrome retries with exponential backoff. If the primary resolver is unreachable, the OS falls back to secondary resolvers (if configured). Browsers also have hardcoded fallback DNS servers in some cases. A DNS timeout is typically 5 seconds per attempt, and browsers may try 2-3 times. That's potentially 10-15 seconds of the user staring at a blank page. This is why DNS reliability matters more than DNS speed for production services.

Common Mistakes

What developers doWhat they should do
Setting high TTLs (24h+) and expecting instant DNS migrations
Existing caches won't refresh until the old TTL expires. If your current TTL is 86400s, changing the record won't reach all users for up to 24 hours regardless of the new TTL.
Lower TTL to 60-300s at least 24 hours BEFORE a migration, so old caches expire
Using preconnect for every third-party origin on the page
Each preconnect opens a full TCP + TLS connection. Too many waste bandwidth and CPU. dns-prefetch is lightweight (just DNS resolution) and safe to use broadly.
Preconnect to 2-4 critical origins. Use dns-prefetch for the rest.
Thinking DNS propagation takes 48 hours as a hard rule
The 48-hour myth comes from old default TTLs. With a 300s TTL, global propagation completes in under 10 minutes. The bottleneck is always the highest TTL in the caching chain.
Propagation time depends on TTL values at each caching layer, not a fixed 48-hour window

Key Takeaways

Key Rules
  1. 1DNS resolution can add 20-200ms before any real network activity begins. On mobile, it can exceed 500ms.
  2. 2Five caching layers (browser, OS, router, ISP resolver, TLD hints) short-circuit most lookups. The full 4-server chain is rarely traversed.
  3. 3TTL controls cache duration. Lower it BEFORE migrations, not during.
  4. 4Use dns-prefetch for third-party domains and preconnect for the 2-4 most critical origins. Don't over-preconnect.
  5. 5DNS-over-HTTPS encrypts queries between you and the resolver, preventing ISP snooping and response tampering.
1/10