From db72944ede90f9583b459d07d2f37f52e9a7417b Mon Sep 17 00:00:00 2001 From: Honey Sharma Date: Fri, 3 Apr 2026 12:02:02 +0530 Subject: [PATCH 1/2] feat: add "What Happens When You Press Enter?" post to series and update overview table --- .../react/series/SeriesAccordion.tsx | 1 - .../series/how-internet-works/index.md | 1 + .../series/how-internet-works/overview.mdx | 2 +- .../series/how-internet-works/press-enter.mdx | 248 ++++++++++++++++++ 4 files changed, 250 insertions(+), 2 deletions(-) create mode 100644 src/content/series/how-internet-works/press-enter.mdx diff --git a/src/components/react/series/SeriesAccordion.tsx b/src/components/react/series/SeriesAccordion.tsx index ec0e251..24dbf5a 100644 --- a/src/components/react/series/SeriesAccordion.tsx +++ b/src/components/react/series/SeriesAccordion.tsx @@ -271,7 +271,6 @@ export const SeriesAccordion: React.FC = ({ const handleCategoryChange = useCallback( (cat: string) => { - console.log("category changed", cat) if (cat === activeCategory) return; setActiveCategory(cat); setOpenIndex(null); diff --git a/src/content/series/how-internet-works/index.md b/src/content/series/how-internet-works/index.md index 4d0462a..95f2ca0 100644 --- a/src/content/series/how-internet-works/index.md +++ b/src/content/series/how-internet-works/index.md @@ -5,4 +5,5 @@ category: Deep Dive order: 1 postOrder: - overview + - press-enter --- diff --git a/src/content/series/how-internet-works/overview.mdx b/src/content/series/how-internet-works/overview.mdx index c6f8a2f..9fda382 100644 --- a/src/content/series/how-internet-works/overview.mdx +++ b/src/content/series/how-internet-works/overview.mdx @@ -134,7 +134,7 @@ The journey is divided into 8 blocks. Each is a logical stage of the trip, cover | # | Post | What you'll learn | |---|------|-------------------| -| 1 | What Happens When You Press Enter? URL Parsing, Caches & the HSTS List | URL anatomy, the browser's pre-network decision tree, HSTS preload, DNS and HTTP cache lookup, Service Worker intercept | +| 1 | [What Happens When You Press Enter? URL Parsing, Caches & the HSTS List](/series/how-internet-works/press-enter) | URL anatomy, the browser's pre-network decision tree, HSTS preload, DNS and HTTP cache lookup, Service Worker intercept | --- diff --git a/src/content/series/how-internet-works/press-enter.mdx b/src/content/series/how-internet-works/press-enter.mdx new file mode 100644 index 0000000..31212c5 --- /dev/null +++ b/src/content/series/how-internet-works/press-enter.mdx @@ -0,0 +1,248 @@ +--- +title: "What Happens When You Press Enter?" +date: "Apr 2026" +pubDate: 2026-04-03 +category: Deep Dive +author: Honey Sharma +summary: Before a single packet leaves your machine, the browser runs four silent checks — HSTS enforcement, Service Worker intercept, DNS cache, HTTP cache. Only if all four miss does it touch the network. This post maps every step. +tags: [networking, browser, hsts, service-worker, dns-cache, http-cache, url, performance] +--- +import URLAnatomy from '../../../components/mdx/URLAnatomy.astro'; +import DecisionFlow from '../../../components/mdx/DecisionFlow.astro'; +import SequenceDiagram from '../../../components/mdx/SequenceDiagram.astro'; +import Terminal from '../../../components/mdx/Terminal.astro'; +import Callout from '../../../components/mdx/Callout.astro'; + +You press Enter. Nothing visible happens yet. + +Underneath, the browser runs a short decision sequence before it touches the network — checking four different places that might let it skip a round-trip to a remote server entirely. Most of the time, at least one check produces a hit. The full network journey we'll trace across this series is the *slow path* — the path taken only when every cache misses. + +This post maps the pre-network phase: what the browser checks, in what order, and what each hit means. + +--- + +## Anatomy of a URL + +The first thing the browser does is parse the text in the address bar. Not just to display it neatly — the parsed result drives every decision that follows: which protocol to use, which host to connect to, which cached responses to look up, which Service Worker to consult. + + + +The browser extracts three things it needs immediately: the **scheme** (determines security requirements and port), the **host** (`www.google.com`) for DNS resolution and cache lookup, and the **origin** (`https://www.google.com`) for Service Worker scope matching. + +### What if it's not a valid URL? + +If what you typed doesn't look like a URL — no scheme, no recognisable TLD, spaces in it — the browser sends it to your configured search engine instead. Chrome's omnibox tries hard to guess: `google.com` without `https://` gets treated as a hostname, but `how does the internet work` goes to Google Search. The heuristics are fuzzy, and the browser occasionally gets it wrong in either direction. + +--- + +## Check 1 — HSTS: The Security Hardcode + +Before opening any connection, the browser asks: *is this domain required to use HTTPS?* + +**HTTP Strict Transport Security (HSTS)** is a mechanism that lets a server tell the browser: "for the next N seconds, only connect to me over HTTPS — never plain HTTP." Once a browser has seen this header from a server, it will silently upgrade any future `http://` request to `https://` without sending the original HTTP request at all. + +The header looks like this: + + + +`max-age=31536000` means 365 days. For the next year, the browser will never send an HTTP request to this domain, regardless of what URL the user types or a link provides. + +### The preload list — no first visit required + +HSTS has a bootstrapping problem: the *very first* visit to a site is still vulnerable, because the browser hasn't received the HSTS header yet. An attacker who intercepts that first HTTP request can downgrade the connection before the user ever reaches HTTPS. + +The **HSTS preload list** solves this. It's a hardcoded list of domains — baked into Chrome, Firefox, Safari, and Edge at compile time — for which HTTPS is enforced *before any network request*, even on first visit. Google, most banking sites, and over 100,000 other domains are on it. + +You can check any domain's status at [hstspreload.org](https://hstspreload.org), or query Chrome's own database directly: + + + + + If a domain isn't on the preload list, the browser only learns about HSTS after the first successful HTTPS connection. That first visit — when the browser goes `http://` and gets redirected to `https://` by the server — is the window of exposure. An attacker on the same network can intercept that first redirect. The preload list is the only complete fix. + + + + Developer gotcha: you've changed your local server's HTTPS setup, things are broken, you clear the cache, reload — still broken. HSTS entries live separately from the HTTP cache. To clear them in Chrome, go to `chrome://net-internals/#hsts` and use the "Delete domain security policies" form. + + +--- + +## Check 2 — Service Worker: The Programmable Gatekeeper + +If a Service Worker is registered for this origin, it intercepts the fetch before the browser checks any network cache. + +A Service Worker is a JavaScript file that runs in a background thread, separate from the main page. It registers itself for an origin (e.g., `https://www.google.com`) and receives a `fetch` event for every request that origin makes — navigations, API calls, image loads, everything. + +```js +// Inside the service worker +self.addEventListener('fetch', (event) => { + event.respondWith( + caches.match(event.request).then((cached) => { + return cached ?? fetch(event.request); + }) + ); +}); +``` + +This is a cache-first strategy: check the SW's own cache (`caches.match`), and only go to the network if nothing matches. The SW has total control — it can serve stale content, synthesize responses, or route different URLs to different caches. + +The browser checks whether a SW is registered for the current URL's origin and, if so, routes the fetch through it. If the SW serves a response from its own cache, the network is never consulted. + +We'll cover Service Worker caching strategies in depth in Block 7. For now, the key point is: if an SW is active and has a cached response, the request never leaves the browser process. + +--- + +## Check 3 — Browser DNS Cache + +If no SW intercepted the request, the browser needs an IP address for the hostname. Before going to the network for DNS, it checks its own DNS cache. + +The browser maintains a small in-memory cache of recent DNS responses. Each entry has a TTL (time-to-live) — a duration set by the DNS server that tells resolvers how long to cache the answer. If the cached entry hasn't expired, the browser uses it directly. + +There are three cache layers the OS checks in order before going to the network: + +| Layer | Where | Typical TTL | +|-------|-------|-------------| +| Browser DNS cache | In-memory, per-process | Respects record TTL | +| OS DNS cache | System-level (`nscd`, `mDNSResponder`) | Varies by OS | +| Router / local resolver | Your home router | Short (often 0) | + +Only if all three miss does a real DNS query leave the machine. We'll trace that query in full in Block 2. + +--- + +## Check 4 — HTTP Cache + +The HTTP cache stores full request/response pairs on disk. If the browser has previously fetched this exact URL and the cached response is still fresh, it can serve it without contacting the server at all. + +Freshness is determined by the `Cache-Control` header. A response with `Cache-Control: max-age=86400` is considered fresh for 24 hours. During that window, any request for the same URL can be served from cache with zero network activity. + +``` +HTTP/2 200 OK +cache-control: max-age=86400, public +content-type: text/javascript +``` + +If the cached response is stale (past its `max-age`), the browser doesn't discard it immediately — it sends a *conditional request* to the server, asking "has this changed?" The server either confirms it's unchanged (returns `304 Not Modified`, saving bandwidth) or sends a fresh copy. We cover this fully in Block 7. + +--- + +## The Complete Decision Sequence + +Here is how all four checks fit together as a temporal sequence. The browser works through them in order — each one a potential exit that avoids everything that follows. + + + +--- + +## The Decision Tree + +The same logic as a decision flow — showing which check leads where, and what a "yes" at each point means for the request. + + + +--- + +## What Actually Reaches the Network + +In practice, most requests hit at least one cache. A first visit to a new site will miss everything and go through the full sequence. A repeat visit to a well-cached site might never touch the network. + +The pattern this creates is why performance work focuses so heavily on caching strategy — every cache hit is a round-trip avoided. The Service Worker gives you programmatic control over the most powerful cache layer. `Cache-Control` headers control the HTTP cache. The HSTS preload list eliminates the HTTP→HTTPS redirect round-trip on first visit. + +When you're debugging "why is my updated file not showing," you're fighting these four layers. The browser isn't broken — it's working exactly as designed. + + + For our running example, we missed every cache — a first visit, no Service Worker registered, no prior DNS or HTTP cache entries. The request now needs a real DNS lookup to turn `www.google.com` into an IP address. That's Block 2. + From 66aabd08a993c924154877ac5aa0039ab423dc4d Mon Sep 17 00:00:00 2001 From: Honey Sharma Date: Fri, 3 Apr 2026 15:48:39 +0530 Subject: [PATCH 2/2] feat: add DNS series content, update build scripts, and refactor SearchModal and content parsing logic --- package.json | 4 +- src/components/react/ui/SearchModal.tsx | 324 +++++++++--------- .../how-internet-works/dns-records-dig.mdx | 278 +++++++++++++++ .../how-internet-works/dns-resolution.mdx | 210 ++++++++++++ .../series/how-internet-works/index.md | 2 + .../series/how-internet-works/overview.mdx | 4 +- src/lib/content.ts | 4 +- 7 files changed, 658 insertions(+), 168 deletions(-) create mode 100644 src/content/series/how-internet-works/dns-records-dig.mdx create mode 100644 src/content/series/how-internet-works/dns-resolution.mdx diff --git a/package.json b/package.json index e6bef37..7e17178 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "format:check": "prettier --check \"src/**/*.{ts,tsx,css,json}\"", "test": "vitest run", "test:watch": "vitest", - "buildcheck": "npm run typecheck && npm run lint && npm run format:check && npm run test && npm run build" + "buildcheck": "npm run typecheck && npm run lint && npm run format && npm run format:check && npm run test && npm run build" }, "dependencies": { "@astrojs/preact": "^4.1.3", @@ -38,4 +38,4 @@ "typescript-eslint": "^8.57.1", "vitest": "^4.1.0" } -} +} \ No newline at end of file diff --git a/src/components/react/ui/SearchModal.tsx b/src/components/react/ui/SearchModal.tsx index 3cdd32e..177e33b 100644 --- a/src/components/react/ui/SearchModal.tsx +++ b/src/components/react/ui/SearchModal.tsx @@ -210,184 +210,186 @@ export const SearchModal: React.FC = () => { return ( <> - {isOpen ? <> - {/* ── Backdrop ── */} -
{ - if (e.target === e.currentTarget) close(); - }} - /> + {isOpen ? ( + <> + {/* ── Backdrop ── */} +
{ + if (e.target === e.currentTarget) close(); + }} + /> - {/* ── Modal panel ── */} -
-
- {/* ── Close button — delayed entrance ── */} - - - {/* ── Scrollable area ── */} -
- {/* ── Search box — padding-top animates center → top ── */} -
+
+ {/* ── Close button — delayed entrance ── */} + + + {/* ── Scrollable area ── */} +
+ {/* ── Search box — padding-top animates center → top ── */}
-

- Search the blog -

-

- Search across all articles — engineering, deep dives, architecture - · - - ⌘K - -  to toggle -

-
+ {/* Subtitle — collapses when user starts typing */} +
+

+ Search the blog +

+

+ Search across all articles — engineering, deep dives, architecture + · + + ⌘K + +  to toggle +

+
- {/* Input */} -
- - - {isLoading && ( -
-
-
- )} - {query && !isLoading && ( - - )} + {/* Input */} +
+ + + {isLoading && ( +
+
+
+ )} + {query && !isLoading && ( + + )} +
-
- {/* ── Results ── */} - {hasSearched && ( - - )} + + + ))} + +

+ {results.length} result{results.length !== 1 ? 's' : ''} +

+ + )} +
+ )} - {/* Bottom spacer when idle */} - {!hasSearched &&
} + {/* Bottom spacer when idle */} + {!hasSearched &&
} +
-
- : null} + + ) : null} ); }; diff --git a/src/content/series/how-internet-works/dns-records-dig.mdx b/src/content/series/how-internet-works/dns-records-dig.mdx new file mode 100644 index 0000000..511e9ed --- /dev/null +++ b/src/content/series/how-internet-works/dns-records-dig.mdx @@ -0,0 +1,278 @@ +--- +title: "DNS Records, dig, Glue Records & Modern DNS" +date: "Apr 2026" +pubDate: 2026-04-17 +category: Deep Dive +author: Honey Sharma +summary: The DNS resolution journey ends at a nameserver holding DNS records. This post covers every record type that matters in practice, how to read and query them with dig, the glue record bootstrap problem, the DNS wire format, and how DoH and DoT encrypt the query. +tags: [networking, dns, dns-records, dig, glue-records, doh, dot, cname, mx, txt] +--- +import PacketDiagram from '../../../components/mdx/PacketDiagram.astro'; +import SequenceDiagram from '../../../components/mdx/SequenceDiagram.astro'; +import Terminal from '../../../components/mdx/Terminal.astro'; +import Callout from '../../../components/mdx/Callout.astro'; + +The previous post traced the DNS resolution journey — a recursive resolver working through root, TLD, and authoritative nameservers to get an answer. But what is that answer, exactly? + +The answer is a **DNS record** — a structured entry in the authoritative nameserver's zone file. There are dozens of record types; seven of them come up constantly in real work. This post covers those seven, shows how to query them with `dig`, explains glue records (the structural fix for a circular dependency at the heart of DNS), walks through the DNS wire format, and looks at how modern DNS adds encryption. + +--- + +## Record Types + +### A — IPv4 Address + +The most fundamental record. Maps a hostname to an IPv4 address. + + + +A single hostname can have multiple A records pointing to different IPs — this is how basic load balancing and geographic routing work at the DNS level. The client picks one (typically the first, though resolvers may vary). + +--- + +### AAAA — IPv6 Address + +The IPv6 equivalent of A. Four times as many characters, the same idea. + + + +Modern browsers perform a "Happy Eyeballs" race: they query for both A and AAAA simultaneously and connect via whichever responds first (preferring IPv6). If only an A record exists, the connection falls back to IPv4. + +--- + +### CNAME — Canonical Name (Alias) + +A CNAME says "this name is an alias for another name." The resolver follows the chain until it finds an A or AAAA record. + +> DiG 9.10.6 <<>> CNAME docs.example.com +;; QUESTION SECTION: +;docs.example.com. IN CNAME + +;; ANSWER SECTION: +docs.example.com. 3600 IN CNAME example.github.io. + +;; Query time: 22 msec +;; SERVER: 8.8.8.8#53(8.8.8.8)`} +/> + + + `CNAME` records cannot coexist with other records at the same name. This makes them illegal at the zone apex (the naked domain like `google.com`) — because the apex must have `NS` and `SOA` records. So you can CNAME `www.google.com` but not `google.com` itself. + + This is a real problem for CDN and hosting setups that require you to point your root domain to a hostname (not an IP). The workaround is a proprietary `ALIAS` or `ANAME` record offered by some DNS providers — they resolve the target at query time and return the resulting IP as if it were an A record. It's not in the DNS standard, but it's widely supported. + + +--- + +### MX — Mail Exchange + +Controls where email for a domain is delivered. MX records include a **priority** number — lower means higher preference. Mail servers try the lowest-priority MX first and fall back to higher values if it's unavailable. + + + +If a domain has no MX record, mail servers fall back to the A record. If a domain has `MX 0 .` (null MX, RFC 7505), it explicitly rejects all email. + +--- + +### TXT — Text Records + +Free-form text attached to a name. Used for everything that doesn't have its own record type: domain verification, SPF anti-spoofing, DKIM public keys, DMARC policy. + + + +The `v=spf1` record tells receiving mail servers which hosts are authorised to send email claiming to be from `google.com`. If you own a domain and send email from it, you need this — otherwise your messages are likely to be marked as spam. + +--- + +### NS — Nameservers + +Delegates authority for a domain to a set of nameservers. These are what the TLD nameserver returns in referrals. + + + +When you register a domain, you set its NS records at your registrar. This is what delegates DNS authority from the TLD registry to your nameservers. Changing NS records triggers a 24–48 hour wait because TLD nameservers cache them at long TTLs. + +--- + +### SOA — Start of Authority + +Every DNS zone has exactly one SOA record. It contains administrative metadata: the primary nameserver, the responsible email address, the zone's serial number (incremented on every change), and timing values for zone transfers. + + + +Reading it left to right: primary NS is `ns1.google.com`, admin contact is `dns-admin.google.com` (the first `.` is `@`), serial is `606836917`, refresh/retry/expire/minimum TTL follow. The serial is what secondary nameservers use to detect that a zone has changed and needs to be re-transferred. + +--- + +## dig: Your DNS Debugger + +Beyond `+short` and basic queries, `dig` has flags worth knowing for debugging. + + + +Use `@` to bypass your system's configured resolver and query a specific one directly. Useful for checking whether a propagation issue is with your ISP's resolver or global. + +> DiG 9.10.6 <<>> A nonexistent.example.com +;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 12345 +;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 0 + +;; AUTHORITY SECTION: +example.com. 3600 IN SOA ns1.example.com. ...`} +/> + +`NXDOMAIN` (Non-Existent Domain) means the name doesn't exist in DNS. The AUTHORITY section returns the SOA record of the parent zone so the resolver knows how long to cache the negative response. + +--- + +## Glue Records: Breaking the Circular Dependency + +Here is a structural puzzle at the heart of DNS. Consider Google's authoritative nameservers: `ns1.google.com`, `ns2.google.com`. The `.com` TLD returns these names in a referral. Your resolver now needs to query `ns1.google.com` — but to do that, it needs to resolve `ns1.google.com` first, which requires querying `google.com`'s authoritative nameserver... which is `ns1.google.com`. + +Circular dependency. + +**Glue records** break the loop. When you delegate a domain to nameservers under that same domain, the registrar requires you to provide the IP addresses of those nameservers. The TLD registry stores those IPs alongside the NS records. The TLD then includes them in the ADDITIONAL section of referral responses — before the resolver needs to look them up. + + + +Glue records only exist when the nameserver hostname is under the domain being delegated. If Google used `ns1.cloudns.net` instead of `ns1.google.com`, there would be no circular dependency and no glue records needed. + +--- + +## The DNS Wire Format + +DNS messages travel over UDP (port 53), typically. Each message — query and response — shares the same binary format. + + + +A few things worth knowing about the wire format: + +- **Name compression** — DNS uses pointer references within the message to avoid repeating long names. `www.google.com` in the answer section typically stores a 2-byte pointer back to where the name appeared in the question section, saving bytes. +- **UDP limit** — traditional DNS messages are limited to 512 bytes over UDP. Responses exceeding this set the TC (truncated) flag, and the client retries over TCP. EDNS0 (RFC 2671) extends this limit to 4096+ bytes, allowing larger responses (like DNSSEC signatures) over UDP. +- **Port 53** — standard DNS uses port 53 for both UDP and TCP. DoH and DoT use different ports (443 and 853 respectively). + +--- + +## DoH and DoT: Encrypting the Query + +Traditional DNS is plaintext UDP. Every DNS query you send is visible to your ISP, your network operator, and anyone observing your traffic. Your ISP can see every domain you look up — even if the subsequent connection is HTTPS. + +Two standards address this: + +**DNS over TLS (DoT)** wraps DNS in a TLS connection on **port 853**. The protocol is still DNS, just encrypted. Configured at the OS level (Android 9+ supports it natively via "Private DNS" settings; on Linux, via `systemd-resolved`). + +**DNS over HTTPS (DoH)** sends DNS queries as HTTPS requests to a resolver's HTTPS endpoint (typically `https://dns.google/dns-query`). From the network's perspective, it's indistinguishable from normal web traffic. Can be configured per-browser (Firefox and Chrome both support it) or at the OS level. + +| | DoT | DoH | +|---|-----|-----| +| Port | 853 (distinct, blockable) | 443 (same as HTTPS) | +| Configured at | OS level | Browser or OS | +| Visibility | Encrypted, but identifiable as DNS | Indistinguishable from HTTPS | +| Provider | System resolver | Browser's chosen resolver | + + + DoH configured in the browser bypasses your OS's DNS settings entirely — including any network-level filtering your employer, school, or parent router applies. This is why some corporate networks block DoH endpoints. The flip side: on a hostile network (public WiFi), DoH in the browser protects your DNS queries even if the network's resolver is manipulating responses. + + + + Our running example used a traditional plaintext DNS query — resolver returned `142.250.80.46`. With DoH or DoT, the resolution journey is identical; only the transport is encrypted. The IP address we got is the same either way. Block 3 picks up here with the physical journey from your laptop to that address. + diff --git a/src/content/series/how-internet-works/dns-resolution.mdx b/src/content/series/how-internet-works/dns-resolution.mdx new file mode 100644 index 0000000..4bdbe76 --- /dev/null +++ b/src/content/series/how-internet-works/dns-resolution.mdx @@ -0,0 +1,210 @@ +--- +title: "DNS Resolution: How google.com Becomes an IP Address" +date: "Apr 2026" +pubDate: 2026-04-10 +category: Deep Dive +author: Honey Sharma +summary: Your browser knows "google.com" but routers only understand IP addresses. DNS is the distributed translation system between the two — a 5-hop conversation that happens in under 20ms. This post traces every step. +tags: [networking, dns, resolver, nameserver, ttl, dig, performance] +--- +import NetworkPath from '../../../components/mdx/NetworkPath.astro'; +import SequenceDiagram from '../../../components/mdx/SequenceDiagram.astro'; +import Terminal from '../../../components/mdx/Terminal.astro'; +import Callout from '../../../components/mdx/Callout.astro'; + +Routers don't speak English. Every packet on the internet needs a numeric destination — an IP address like `142.250.80.46`. But humans use names like `google.com`. + +**DNS** (Domain Name System) is the distributed database that translates one into the other. When your browser needs to connect to `google.com`, it runs a DNS lookup — a short conversation with a chain of servers that collectively know where every domain in the world points. The whole process typically completes in 20–80 milliseconds. + +This post traces that conversation, hop by hop. + +--- + +## The Four Actors + +DNS resolution involves four distinct types of server. Understanding who each one is makes the rest of the post click. + + + +**Your browser (stub resolver)** — knows only one thing: the address of a recursive resolver to ask. It delegates all the work. + +**Recursive resolver** — the server that actually does the resolution work. It queries root, TLD, and authoritative nameservers on your behalf, caches the results, and returns the final answer. Your ISP provides one by default; `8.8.8.8` (Google) and `1.1.1.1` (Cloudflare) are popular alternatives. + +**Root nameservers** — the starting point for any resolution. They don't know where `google.com` is, but they know who handles `.com`. There are 13 root nameserver addresses (labeled a through m), each backed by an anycast cluster of hundreds of physical servers worldwide. + +**TLD nameservers** — responsible for top-level domains like `.com`, `.org`, `.io`. They don't know Google's IP, but they know which nameservers are authoritative for `google.com`. + +**Authoritative nameservers** — the final authority for a domain. Google operates `ns1.google.com` through `ns4.google.com`. These servers hold the actual DNS records and return the definitive answer. + + + The 13 root nameserver *addresses* (a.root-servers.net through m.root-servers.net) correspond to 13 IP addresses — but each is served by a large anycast cluster. There are over 1,600 root server instances distributed across the globe. Your query reaches the nearest one, usually within a few milliseconds. + + +--- + +## The Full Resolution Journey + +With the four actors clear, here is the complete lookup for `www.google.com` — including the cache checks that make real-world DNS fast. + + + +### Step by step + +**Step 1** — Your browser sends the query to its configured recursive resolver. This is a UDP packet (usually, though TCP is used for larger responses). + +**Step 2** — The resolver checks its own cache. If it resolved `www.google.com` recently and the TTL hasn't expired, it returns immediately — no further network calls. For a first lookup, the cache misses. + +**Step 3** — The resolver asks a root nameserver. Root doesn't know Google's IP, but it knows which servers handle `.com`. It returns a referral to the `.com` TLD nameservers. + +**Step 4** — The resolver asks the `.com` TLD nameserver. TLD doesn't know the IP either, but it knows `ns1.google.com` is authoritative for `google.com`. It returns a referral — including **glue records** (the IP address of `ns1.google.com` itself, to avoid a circular dependency). More on glue records in the next post. + +**Step 5** — The resolver asks Google's authoritative nameserver directly. This server knows the answer: `142.250.80.46` with TTL 300 (5 minutes). + +**Step 6** — The resolver caches the answer and returns it to your browser. + +Total round trips: 3 network hops for a cold cache. In practice, root and TLD answers are cached for hours or days, so most real-world lookups require only the final hop to the authoritative server. + +--- + +## TTL: Why DNS Changes Take Time + +Every DNS record has a TTL — a number (in seconds) set by the domain owner that tells resolvers how long to cache the answer. + +``` +www.google.com. 300 IN A 142.250.80.46 + ↑↑↑ + TTL: 300 seconds (5 minutes) +``` + +Once a resolver has cached this record, it will serve that cached answer for up to 300 seconds without asking Google's nameserver again. If Google changes their IP during that window, resolvers serving cached answers will still point to the old address until their cached entry expires. + +This is what "DNS propagation" means — not a global sync that pushes changes out, but a collection of TTL timers draining across thousands of resolvers worldwide. A TTL of 300 seconds propagates fully in about 5 minutes. A TTL of 86400 (24 hours) means some users may see the old address for up to 24 hours. + +**Practical implication:** If you're planning a domain migration, lower the TTL to 60 seconds a day or two before switching. After the switch, raise it back. This minimises the exposure window. + + + "DNS propagation" is a common but misleading term. Changes don't propagate outward — they wait to be discovered. Each resolver keeps serving its cached answer until its TTL expires, then asks the authoritative server for a fresh answer. The only way to speed this up is to lower your TTL before making the change. + + +--- + +## Reading a Real DNS Response + +The `dig` command is the standard tool for querying DNS. Every developer who works with domains should know it. + +> DiG 9.10.6 <<>> www.google.com +;; global options: +cmd +;; Got answer: +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 54321 +;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;www.google.com. IN A + +;; ANSWER SECTION: +www.google.com. 300 IN A 142.250.80.46 + +;; Query time: 18 msec +;; SERVER: 8.8.8.8#53(8.8.8.8) +;; WHEN: Thu Apr 03 2026 +;; MSG SIZE rcvd: 59`} +/> + +Reading the output: + +- **`status: NOERROR`** — the query succeeded. Other values: `NXDOMAIN` (domain doesn't exist), `SERVFAIL` (nameserver error), `REFUSED`. +- **`flags: qr rd ra`** — `qr` means this is a response (not a query), `rd` means recursion was desired, `ra` means recursion is available at this resolver. +- **QUESTION SECTION** — what was asked: the A record for `www.google.com`. +- **ANSWER SECTION** (highlighted) — the response: TTL 300, type A, value `142.250.80.46`. +- **`SERVER: 8.8.8.8`** — which resolver answered. On your machine, this will be your configured DNS server. +- **`Query time: 18 msec`** — resolver already had this cached. A cold cache would be 60–200ms. + +--- + +## Watching the Hops Live + +The `+trace` flag tells `dig` to perform the full recursive resolution itself, showing each nameserver hop. + + + +Each highlighted `Received` line marks a hop: +- **Hop 1** (14ms) — root nameserver returns the `.com` TLD NS addresses +- **Hop 2** (22ms) — `.com` TLD returns Google's authoritative NS + glue A records for `ns1–ns2.google.com` +- **Hop 3** (12ms) — Google's authoritative NS returns the final answer: `142.250.80.46` +- **Final answer** (8ms) — the authoritative answer, TTL 300 + +Total: ~56ms for a completely cold cache. Subsequent lookups skip the first two hops entirely. + +Notice lines 15–16: `ns1.google.com. 172800 IN A 216.239.32.10` — these are the glue records. The TLD included the IP addresses of Google's nameservers in the ADDITIONAL section so the resolver doesn't need to make a separate DNS lookup just to reach `ns1.google.com`. The next post explains glue records in full. + + + We now have `142.250.80.46` — the IP address of Google's nearest edge server. The DNS lookup is complete. Block 3 picks up here: the physical journey from your laptop to that server over WiFi, through your router, across your ISP, and onto the internet backbone. + diff --git a/src/content/series/how-internet-works/index.md b/src/content/series/how-internet-works/index.md index 95f2ca0..4940227 100644 --- a/src/content/series/how-internet-works/index.md +++ b/src/content/series/how-internet-works/index.md @@ -6,4 +6,6 @@ order: 1 postOrder: - overview - press-enter + - dns-resolution + - dns-records-dig --- diff --git a/src/content/series/how-internet-works/overview.mdx b/src/content/series/how-internet-works/overview.mdx index 9fda382..74242c8 100644 --- a/src/content/series/how-internet-works/overview.mdx +++ b/src/content/series/how-internet-works/overview.mdx @@ -142,8 +142,8 @@ The journey is divided into 8 blocks. Each is a logical stage of the trip, cover | # | Post | What you'll learn | |---|------|-------------------| -| 2 | DNS Resolution: How google.com Becomes an IP Address | Recursive resolution journey, root/TLD/authoritative nameservers, resolver caches, TTL, reading a DNS response | -| 3 | DNS Records, `dig`, Glue Records & Modern DNS | A, AAAA, CNAME, MX, TXT, NS, SOA records; `dig` command walkthrough; glue records; DNS over HTTPS and TLS | +| 2 | [DNS Resolution: How google.com Becomes an IP Address](/series/how-internet-works/dns-resolution) | Recursive resolution journey, root/TLD/authoritative nameservers, resolver caches, TTL, reading a DNS response | +| 3 | [DNS Records, `dig`, Glue Records & Modern DNS](/series/how-internet-works/dns-records-dig) | A, AAAA, CNAME, MX, TXT, NS, SOA records; `dig` command walkthrough; glue records; DNS over HTTPS and TLS | --- diff --git a/src/lib/content.ts b/src/lib/content.ts index 99bb5f4..6b9c782 100644 --- a/src/lib/content.ts +++ b/src/lib/content.ts @@ -138,9 +138,7 @@ export function getRelatedPosts( export async function parseSeriesCollection(): Promise { const { indexEntries, postEntries } = await splitSeriesEntries(); - const sorted = [...indexEntries].sort( - (a, b) => (a.data.order ?? 999) - (b.data.order ?? 999), - ); + const sorted = [...indexEntries].sort((a, b) => (a.data.order ?? 999) - (b.data.order ?? 999)); return sorted.map((index) => { const seriesSlug = index.slug.replace('/index', '');