DEV Community

Cover image for What Actually Happens When You Resolve a Domain Name
Syed Anzar
Syed Anzar

Posted on

What Actually Happens When You Resolve a Domain Name

What Actually Happens When You Resolve a Domain Name

You type example.com in your browser. Milliseconds later, the page loads. What happened in between?

Most developers know DNS "turns names into IPs." Few can explain what actually happens when the cache is cold. Let's trace the full path—from your stub resolver to the root servers, through TLDs, to the authoritative answer—and the protocol details that make it work.


The Big Picture

DNS resolution is a distributed delegation chain. No single server knows everything. Each server knows only its zone and where to find the next level down.

Client → Stub Resolver → Recursive Resolver → Root → TLD → Authoritative → Answer
Enter fullscreen mode Exit fullscreen mode

Your browser doesn't talk to root servers. It talks to a stub resolver (OS DNS client). That stub forwards to a recursive resolver (your ISP, 1.1.1.1, 8.8.8.8, or a local Unbound/BIND). The recursive resolver does the heavy lifting.


Step 0: The Cache Check

Before any network traffic, the recursive resolver checks its cache:

  1. Exact match? Return immediately.
  2. CNAME chain? Follow it, then return the final target.
  3. Negative cache (NXDOMAIN/NODATA)? Return "doesn't exist" without querying upstream.
  4. Partial match (NS records for a parent zone)? Start from there—this is the closest delegation point.

If the cache is cold (resolver just restarted), it has only root hints: a built-in list of 13 root server names (a.root-servers.net through m.root-servers.net) and their IPs.


Step 1: Priming — Getting the Real Root List

Root hints are hints. They can be stale. Before trusting them, the resolver sends a priming query (RFC 8109):

QNAME: . (root)
QTYPE: NS
RD: 0 or 1 (undefined for priming)
EDNS(0): YES, UDP payload ≥ 1024 bytes
DO bit: optionally set
Enter fullscreen mode Exit fullscreen mode

Sent to one root hint address (selected randomly to spread load). The root responds with:

  • Answer section: NS RRset for . (the 13 root server names)
  • Additional section: A/AAAA records for those names (glue records)
  • AA bit = 1: authoritative answer

This primes the resolver with the current, authoritative root NS set and addresses. TTL is typically 6 days (518,400 seconds).


Step 2: The Descent — Iterative Queries

Now the resolver has a working root NS set. It begins iterative resolution (RD=0) for www.example.com:

Query to Root (.)

QNAME: www.example.com (or minimized: com — see QNAME minimization below)
QTYPE: A (or AAAA)
RD: 0
Enter fullscreen mode Exit fullscreen mode

Root doesn't know example.com. It's only authoritative for .. It responds with a referral:

  • Authority section: NS records for .com (the TLD nameservers)
  • Additional section: Glue A/AAAA for those NS names (e.g., a.gtld-servers.net)

Query to TLD (.com)

Resolver picks one .com nameserver, sends:

QNAME: www.example.com (or minimized: example.com)
QTYPE: A
RD: 0
Enter fullscreen mode Exit fullscreen mode

TLD server isn't authoritative for example.com either. Another referral:

  • Authority: NS records for example.com (your domain's authoritative nameservers)
  • Additional: Glue records if the NS names are inside example.com (e.g., ns1.example.com)

Query to Authoritative (example.com)

Resolver picks one authoritative NS for example.com:

QNAME: www.example.com
QTYPE: A
RD: 0
Enter fullscreen mode Exit fullscreen mode

This server is authoritative. It responds with:

  • Answer section: The A record (e.g., 93.184.216.34)
  • AA bit = 1: "I own this data"
  • TTL: How long to cache (e.g., 3600 seconds)

Resolver caches the answer, the NS records, and glue—and returns it to the stub resolver.


QNAME Minimization: Privacy by Design (RFC 7816 / 9156)

Traditional resolvers send the full QNAME (www.example.com) to every server in the chain. The root doesn't need www.example.com—it only needs .com. The TLD only needs example.com.

QNAME minimization sends only what each server needs:

Server Traditional Query Minimized Query
Root www.example.com A com NS
TLD www.example.com A example.com NS
Auth www.example.com A www.example.com A

The resolver tracks zone cuts (delegation boundaries) in its cache. When it doesn't know where the cut is, it probes one label at a time (NS queries), caching NODATA responses to learn the boundary.

Why it matters: Root servers see only com, not your full browsing history. Reduces data exposure to authoritative operators. Also enables aggressive negative caching (RFC 8020): if .example doesn't exist, nothing under it exists either.


EDNS(0): Breaking the 512-Byte Limit (RFC 6891)

Original DNS: 512-byte UDP max. DNSSEC responses (with RRSIG, DNSKEY, NSEC) routinely exceed this.

EDNS(0) adds an OPT pseudo-record in the additional section:

NAME: . (root)
TYPE: OPT (41)
CLASS: Requestor's UDP payload size (e.g., 4096)
TTL: Extended RCODE (high 8 bits) + flags (DO bit in bit 15)
RDATA: Variable {option-code, option-length, option-data} tuples
Enter fullscreen mode Exit fullscreen mode

Key fields:

  • UDP payload size: "I can receive this many bytes." Default 4096. Values < 512 treated as 512.
  • DO bit (DNSSEC OK): "Send me DNSSEC records."
  • Options: COOKIE (RFC 7873), ECS (RFC 7871), PADDING, etc.

If a responder can't fit the answer in the advertised size, it sets TC=1 (truncated). Resolver retries over TCP.

Middlebox warning: Firewalls/load balancers that strip OPT records or block >512-byte UDP break DNSSEC and large responses. RFC 6891 says they MUST NOT do this—but they do.


The Kaminsky Attack & Why Source Port Randomization Matters

In 2008, Dan Kaminsky showed how to poison a resolver's cache off-path.

The vulnerability: Resolvers accepted the first response matching:

  • Query name + QTYPE
  • 16-bit Transaction ID (TXID)
  • Source/destination IP
  • Source/destination port (often fixed at 53)

Attacker floods resolver with spoofed responses, guessing TXID. With fixed source port, only 65,536 guesses needed—trivial.

The fix: Source port randomization (RFC 5452). Resolver picks a random ephemeral source port per query. Now attacker must guess TXID (16 bits) + source port (~15 bits) = ~31 bits of entropy. At 50,000 packets/sec, success probability drops from ~100% in seconds to ~64% in 24 hours with ~0.4 TB of traffic.

Remaining weaknesses:

  • NAT devices with predictable port allocation (sequential, port-preserving) reduce entropy.
  • ICMP side channels can leak source ports on some OS/resolver combos (CCS 2020).
  • Real fix: DNSSEC (cryptographic validation). Port randomization is a band-aid; DNSSEC is the cure.

DNSSEC: The Chain of Trust

DNSSEC signs zones with asymmetric crypto. Each zone has:

  • KSK (Key Signing Key): Signs the DNSKEY RRset. Published as DS record in parent zone.
  • ZSK (Zone Signing Key): Signs all other records in the zone.

Validation flow for www.example.com:

  1. Resolver gets A record + RRSIG from example.com authoritative.
  2. Resolver gets DNSKEY (ZSK) for example.com + RRSIG.
  3. Resolver gets DS record for example.com from .com TLD + RRSIG.
  4. Resolver gets DNSKEY (KSK) for .com from .com authoritative + RRSIG.
  5. Resolver gets DS record for .com from root + RRSIG.
  6. Resolver gets DNSKEY for root (trust anchor, pre-configured).

If every signature verifies up to the trust anchor → AD bit = 1 (Authenticated Data). If any break → SERVFAIL.

NSEC/NSEC3: Prove non-existence. NSEC lists "next secure name"—enables zone walking. NSEC3 hashes names (salted) to prevent enumeration.


Common Misconceptions

Myth Reality
"My browser queries root servers" Browser → stub → recursive resolver. Recursive does the descent.
"DNS is a single database" Distributed delegation. Each zone owns its data.
"TTL is a guarantee" TTL is a hint. Resolvers may cap it (e.g., 2-day max). Negative TTL from SOA MINIMUM.
"CNAME at apex works" RFC 1912: CNAME at zone apex breaks NS/SOA. Use ALIAS/ANAME (non-standard) or flattening (Cloudflare).
"DNSSEC encrypts queries" DNSSEC signs data (authenticity/integrity). It does NOT encrypt. Use DoH/DoT/DoQ for privacy.
"13 root servers = 13 machines" 13 identities (a–m.root-servers.net). Each is a global anycast constellation (hundreds of physical servers).

Practical Takeaways

  1. Run a local validating resolver (Unbound, Knot Resolver). Cuts latency, enables DNSSEC validation, supports QNAME minimization.
  2. Enable DNSSEC validation. It's the only real protection against cache poisoning.
  3. Understand TTL caps. Don't rely on 60-second TTLs for fast failover—resolvers may ignore them.
  4. Monitor your glue. If you rename NS hosts inside your zone, update glue at the registrar before the old records expire.
  5. Use dig +trace to see the actual delegation chain. dig +dnssec to see validation.

The Mental Model

DNS isn't a lookup table. It's a tree of delegations, each signed (optionally), cached at every level, with privacy minimizations and protocol extensions layered on top. The next time you hit example.com, you're not "querying a database." You're walking a chain of trust from root to leaf—one referral at a time.


Tags: dns, networking, systems, security

Top comments (0)