
You type domain names into your browser dozens of times a day — github.com, google.com, coolboiledwater.cn. You press Enter and the page loads. It happens so naturally that you almost never think: what happened in the first few milliseconds before I saw anything?
If you open Chrome DevTools' Network panel, the first request isn't HTTP or even HTTPS — it's an invisible DNS query. Before your browser can say "hello" to the server, it must answer one question:
What's the IP address of github.com?
That's the mission of DNS (Domain Name System). It's the internet's phonebook — translating human-readable names into machine-routable numbers. Without DNS, you'd need to memorize 140.82.121.3 instead of github.com, and the internet would devolve into a memorized list of IPs.
This article dissects DNS from top to bottom — from root servers to your browser cache, from UDP datagrams to DoH encrypted tunnels, from a domain's registration to its global reachability.
Open a terminal and type:
ping coolboiledwater.cnThe first line of output:
PING coolboiledwater.cn (152.136.173.183): 56 data bytesNotice — you typed a domain name, but ping has already substituted it with an IP address 152.136.173.183. This translation happens so fast (typically 5-50ms) that you barely notice it exists.
Now disconnect Wi-Fi and try again:
ping: cannot resolve coolboiledwater.cn: Unknown host"Unknown host" — not "network unreachable," but "I can't even get the IP address." DNS is the first link in the entire network request chain. Break it, and everything downstream — TCP, TLS, HTTP — is dead on arrival.
Unfold the entire timeline:
You type coolboiledwater.cn and press Enter
│
├─ 0ms: Browser checks its own DNS cache → miss
├─ 0ms: OS checks /etc/hosts → miss
├─ 1ms: OS sends UDP query to DNS recursive resolver → "What's coolboiledwater.cn's IP?"
│ ↓
│ Recursive resolver asks around the world on your behalf...
│ ↓
├─ 20ms: Recursive resolver returns answer: "152.136.173.183"
├─ 20ms: Browser gets IP, starts TCP three-way handshake
├─ 40ms: TCP connected, starts TLS handshake
├─ 70ms: Secure channel established, sends HTTP request
└─ 90ms: Page starts loadingDNS took about 20ms — over 20% of the entire request chain. And those 20ms happen before anything else can begin: no DNS, no TCP, no TLS, no HTTP.
Before DNS, the internet used a text file called hosts to manually map names to IPs:
# /etc/hosts
127.0.0.1 localhost
192.168.1.100 my-printer
93.184.216.34 example.comOne file per machine, manually updated by administrators. This worked when ARPANET had only a few dozen computers. But as the internet grew to millions — then billions — of hosts, this approach hit three fatal limits:
| Problem | Impact |
|---|---|
| Scalability | Every new or changed domain requires updating hosts files on every machine worldwide — impossible |
| Uniqueness | No central authority to allocate names; two people could "register" the same name |
| Freshness | When an IP changes, there's no real-time way to sync all hosts files |
DNS was designed by Paul Mockapetris in 1983 (RFC 882/883). The core insight: make the problem scalable by turning the flat namespace into a hierarchical, delegated, distributed system.
The essential problem DNS solves: at internet scale, efficiently and reliably map human-readable names to machine-routable IP addresses.
The domain namespace is an inverted tree. Understanding its hierarchy is the key to understanding DNS:
[Root .]
┌───────────┼───────────┐
[com] [org] [cn] ← Top-Level Domain (TLD)
┌─────┼─────┐ │ ┌────┼────┐
[github] [google] [wikipedia] [gov] [com] [coolboiledwater]
│ │ │
[api] [mail] [www]., usually omitted. A fully qualified domain is github.com. (note the trailing dot). 13 root server clusters (a.root-servers.net through m.root-servers.net), distributed globally via anycast, manage the entry point to the entire tree..com, .org, .net, .cn, .io... divided into generic TLDs (gTLDs like .com) and country-code TLDs (ccTLDs like .cn).github in github.com, coolboiledwater in coolboiledwater.cn.api.github.com, docs.github.com.This hierarchy is strikingly similar to filesystem paths:
| Filesystem | DNS |
|---|---|
/ | . (root) |
/usr/ | com. |
/usr/local/ | github.com. |
/usr/local/bin/ | api.github.com. |
Both use progressive delegation: the root knows who manages com, com knows who manages github, and github knows how its subdomains are configured. Nobody needs to know everything.
DNS isn't "one server" — it's a distributed system of four distinct roles:
① Recursive Resolver
Your ISP / 8.8.8.8 / 1.1.1.1
"Ask on your behalf, return the final answer"
│
┌───────────────┼───────────────┐
↓ ↓ ↓
② Root Server ③ TLD Server ④ Authoritative Server
"Ask com" "Ask github" "github.com's IP is..."① Recursive Resolver: The server you talk to directly. It asks the world on your behalf and returns the final answer. Google's 8.8.8.8, Cloudflare's 1.1.1.1, and China's 114.114.114.114 are recursive resolvers. Your router's DNS setting typically points to your ISP's recursive resolver.
② Root Name Server: 13 clusters (deployed as thousands of instances via anycast). It does not directly answer a domain's IP. Instead, it tells you: "Who manages the .com TLD? Go ask them."
③ TLD Server: Manages delegation information for all second-level domains under a specific TLD. Also doesn't give the final IP — it says: "Which servers manage DNS for github.com? Go there."
④ Authoritative Name Server: The ultimate "answer holder." It stores the actual DNS records (A records, MX records, etc.) for a domain and is the only role that can definitively say: "github.com's IP is 140.82.121.3."
Your browser needs github.com's IP address. The recursive resolver walks this path on your behalf:
You Recursive Resolver Root TLD Auth
│ │ │ │ │
│ ① "github.com's IP?" ─────────────────→ │ │ │ │
│ │ │ │ │
│ │ ② "who runs │ │ │
│ │ .com?" │ │ │
│ │─────────────→│ │ │
│ │ │ │ │
│ │ ③ "ask these │ │ │
│ │ TLD │ │ │
│ │ servers" │ │ │
│ │←─────────────│ │ │
│ │ │ │ │
│ │ ④ "where's │ │ │
│ │ github. │ │ │
│ │ com's │ │ │
│ │ DNS?" │ │ │
│ │─────────────────────────→│ │
│ │ │ │ │
│ │ ⑤ "ask these │ │ │
│ │ auth DNS" │ │ │
│ │←─────────────────────────│ │
│ │ │ │ │
│ │ ⑥ "github.com A?" │ │
│ │────────────────────────────────────→│
│ │ │ │ │
│ │ ⑦ "140.82. │ │ │
│ │ 121.3" │ │ │
│ │←────────────────────────────────────│
│ │ │ │ │
│ ⑧ "140.82.121.3" ←───────────────────── │ │ │ │
│ │ │ │ │
│ (cache this result, TTL=600s) │ │ │ │Two query modes are at play:
In daily life, you only make recursive queries. But dig +trace lets you simulate the recursive resolver, walking through iterative queries step by step.
DNS primarily uses UDP, for reasons discussed in the UDP article: a typical DNS query is only a few dozen bytes. Using TCP — three-way handshake (1.5 RTT) + query (0.5 RTT) + teardown — would make the connection setup heavier than the data itself.
But DNS doesn't exclusively use UDP. These cases trigger fallback to TCP:
Normal query:
Client ──UDP: "github.com A?"──→ Resolver (a few dozen bytes)
Client ←──UDP: "140.82.121.3"── (a few dozen bytes)
Response too large (TC=1):
Client ──UDP: "long query"──→ Server
Client ←──UDP: TC=1 ─────── "Too long, retry with TCP"
Client ──TCP: handshake ──→
Client ──TCP: same query ──→ Server
Client ←──TCP: full responseDNS is more than "domain → IP." A domain can host dozens of different record types. Here are the ones front-end developers encounter most often:
| Record Type | Full Name | Purpose | Example |
|---|---|---|---|
| A | Address | Domain → IPv4 | github.com. A → 140.82.121.3 |
| AAAA | Quad-A | Domain → IPv6 | github.com. AAAA → 2a03:2880:... |
| CNAME | Canonical Name | Alias → canonical name | www.github.com. CNAME → github.com. |
| MX | Mail Exchange | Mail server | github.com. MX → aspmx.l.google.com. |
| NS | Name Server | Authoritative DNS | github.com. NS → dns1.p08.nsone.net. |
| TXT | Text | Arbitrary text (SPF/DKIM/verification) | github.com. TXT → "v=spf1 ..." |
| SOA | Start of Authority | Domain admin info | Primary DNS, admin email, serial number |
A vs. CNAME trap: CNAME is an alias — if you set example.com as a CNAME pointing to other.com, then example.com cannot have any other records (MX, TXT, etc.). For apex (root) domains, avoid CNAME; use A records or provider-specific ALIAS/ANAME solutions.
TXT records in practice: Front-end deployments hit these often — adding _acme-challenge.example.com TXT → "xxx" to prove domain ownership for SSL certificates; configuring SPF/DKIM/DMARC records for email authentication.
If every github.com visit required the full 8-step root-to-authoritative walk, the internet would be unbearably slow. DNS relies on multi-layer caching to solve this:
Your Browser
│ ① Browser has its own DNS cache (chrome://net-internals/#dns)
↓
Operating System
│ ② OS-level cache (macOS: mDNSResponder, Linux: systemd-resolved)
│ ③ /etc/hosts file (highest priority!)
↓
Router
│ ④ Home router has DNS cache too
↓
ISP Recursive Resolver (8.8.8.8)
│ ⑤ Caches results for millions of popular domains
│ ⑥ Only walks root→TLD→authoritative on cache miss
↓
Authoritative DNS Server
⑦ Ultimate data sourceEach cache layer looks at the record's TTL (Time to Live) to decide how long to keep it:
github.com. 600 IN A 140.82.121.3
↑
TTL = 600 seconds = 10 minutesTTL is set by the authoritative DNS administrator. Shorter TTL → IP changes propagate faster, but more query load. Longer TTL → higher cache hit rates, but IP changes take longer to take effect.
CDN and DNS working together is a foundation of front-end performance: CDNs use DNS for global load balancing — the same domain cdn.example.com resolves to a Beijing edge node IP for users in China and a New York IP for users in the US. This "geo-awareness" is achieved by authoritative DNS returning different IPs based on the query's origin.
Traditional DNS is plaintext UDP. This means:
Two modern solutions address this:
DoT (DNS over TLS, RFC 7858): DNS queries wrapped in a TLS connection, port 853. Similar to HTTPS — outsiders can only see you're talking to 1.1.1.1, not what you're asking. OS-level support (Android 9+ has native Private DNS).
DoH (DNS over HTTPS, RFC 8484): DNS queries wrapped in HTTPS requests, port 443. Indistinguishable from regular web browsing — traffic analysis cannot distinguish DNS requests from web browsing. Browser-level support (built into Chrome and Firefox).
Traditional DNS (plaintext):
You ──UDP:53── "github.com IP?" ──→ ISP DNS
↑ ISP can see, can modify
DoT (DNS over TLS):
You ──TCP:853 TLS──────── "github.com IP?" ──→ Cloudflare DNS
↑ Only sees: talking to 1.1.1.1
DoH (DNS over HTTPS):
You ──TCP:443 HTTPS── POST /dns-query ──→ Cloudflare DNS
Body: "github.com IP?"
↑ Looks like normal HTTPS trafficImpact on front-end development: If your users enable DoH in Chrome, the browser's DNS resolution may bypass the system DNS and /etc/hosts, going directly to a DoH server. In local development, this can cause test domain resolution failures. Chrome's strategy: if the system has a "canary domain" (internal test domain), DoH is automatically disabled.
Finally, let's trace the complete lifecycle of coolboiledwater.cn:
1. You purchase "coolboiledwater.cn" from a registrar (e.g., Alibaba Cloud DNS)
→ Registrar submits the registration to .cn's registry
→ .cn TLD server adds NS record: coolboiledwater.cn → your designated authoritative DNS
2. You add an A record in your DNS management panel:
coolboiledwater.cn A → 152.136.173.183
→ Authoritative DNS server stores this record
3. The TLD server now has your NS delegation:
coolboiledwater.cn NS → dns1.xxx.com.
→ Recursive resolvers asking ".cn TLD" know where to go next
4. Someone visits your domain for the first time:
→ Recursive resolver walks root→TLD→authoritative to get the IP
→ Caches it for 600 seconds
5. Subsequent visitors:
→ Hit the cache directly, millisecond response
6. You change your server's IP:
→ Update the A record with the new IP
→ Wait for TTL (600s) to expire globally
→ New requests reach authoritative DNS and get the new IPQuery various record types with dig
# A record
dig github.com A
# AAAA record (IPv6)
dig github.com AAAA
# MX record (mail server)
dig github.com MX
# NS record (authoritative DNS)
dig github.com NS
# ALL records (most servers have disabled this)
dig github.com ANY
# Short format, answer only
dig github.com A +shortStudy the output fields. The ;; ANSWER SECTION contains your answer; TTL tells you the cache duration.
Trace full resolution with dig +trace
dig +trace github.comThis command simulates the recursive resolver's full workflow. You'll see:
.com TLD server listgithub.com's authoritative DNS server listEach hop has clear "WHO ASKED" and "WHO ANSWERED" indicators.
Test the hosts file's priority
# View current hosts file
cat /etc/hosts
# Temporarily add a fake entry (requires sudo)
sudo sh -c 'echo "127.0.0.1 example.com" >> /etc/hosts'
# Ping it
ping example.com
# Output: PING example.com (127.0.0.1)
# Clean up (edit /etc/hosts to remove the line)Note: even though DNS says example.com is 93.184.216.34, the hosts file takes priority.
Compare public DNS resolver speeds
# Google DNS
dig @8.8.8.8 github.com +stats | grep "Query time"
# Cloudflare DNS
dig @1.1.1.1 github.com +stats | grep "Query time"
# China 114 DNS
dig @114.114.114.114 github.com +stats | grep "Query time"Compare query times. If the result is cached (Query time < 5ms), you're measuring cache speed.
Capture DNS traffic with tcpdump
# Capture all DNS (UDP port 53) traffic
sudo tcpdump -i en0 'udp port 53' -n -v
# In another terminal
dig github.comYou'll see the query UDP datagram go out and the response come back. If the query triggers TCP fallback (response > 512 bytes), you can also capture DNS TCP traffic:
sudo tcpdump -i en0 'tcp port 53' -n -vSet up a local DNS server with dnsmasq
Quickly set up local DNS caching + custom domains:
# Install (macOS)
brew install dnsmasq
# Configure /usr/local/etc/dnsmasq.conf
# address=/my-local.dev/127.0.0.1
# Now my-local.dev always resolves to 127.0.0.1
# Start
sudo brew services start dnsmasq
# Set system DNS to 127.0.0.1All DNS queries now hit the local cache first, dramatically reducing latency. For front-end local dev, using dnsmasq to point .dev/.local domains to 127.0.0.1 is more flexible than editing /etc/hosts — it supports wildcard subdomains.
Understand CDN geo-routing via DNS
From VPS instances in different regions, or using a VPN, run dig against a CDN domain:
dig cdn.jsdelivr.net ACompare the IPs returned from Beijing vs. Singapore — they're likely different (CDN GeoDNS returns the nearest edge node IP based on query origin).
.) to TLD (.com) to second-level (github.com) to subdomain (api.github.com).This chapter gave you the internet's phonebook — how DNS translates names into addresses. But the IP returned by DNS is only the first step. After getting the IP, the browser performs a TCP three-way handshake, then establishes a TLS encrypted channel, and only then sends the first HTTP request.
The next article returns to the HTTP protocol itself — tracing its evolution from the spartan simplicity of HTTP/0.9, through the structured improvements of HTTP/1.0, to the persistent connections and caching innovations that made HTTP/1.1 the backbone of the modern web.