 Command

Pranesh Nikhar's personal site. Vim-style keybinds for navigation; theme + font pickers below.

Theme
 Font Body Code
Reader
Keybinds
Navigation
j / ↓ Next item k / ↑ Previous item g First item in region G Last item in region zz Center focused item h / l Move left/right region ] / [ Next/previous heading } / { Next/previous block d / u Half-page down/up
Layout
<zh> / <zl> Toggle left/right sidebar <zr> Toggle reader view <zj> / <zk> Focus main/navbar <S-h/j/k/l> Focus left/main/navbar/right ⌃H / ⌃L Focus left/right sidebar ⌃J / ⌃K Focus main/navbar ⇧C / ⇧E Collapse / expand all sections
Dialogs
⌃P / : Command palette ⌃X Theme picker / Search ? Show keybinds Esc / ⌃C Close dialog
History
n Next document b Previous document ⌃O History back ⌃I History forward
 Search
about: Pranesh Nikhar about/more: 🪪 More docs/test: Docs Test ideas: 💡 Ideas more: ➕ More now: Now posts: 📬 Posts projects: 📚 Projects webtui: Style posts/agentic-eda: AgenticEDA — Automated Exploratory Data Analysis with LangGraph posts/cap-theorem-outage-story: CAP Theorem with a Real Outage Story posts/codepilot: CodePilot — From Requirements to Deployable FastAPI Backend posts/common-auth-mistakes: Common Auth Mistakes Developers Make posts/compiled-vs-jit-vs-interpreted: Why Is X Language Fast or Slow? — Compiled vs JIT vs Interpreted posts/cs-degree-gaps: Things CS Degrees Don't Teach You posts/cve-2025-breach-analysis: CVE-2025 Breach Analysis — Midnight Blizzard and the 16 Billion Credential Leak posts/fixloop: FixLoop — AI Agent Loop for Self-Correcting Code posts/functional-vs-oop: Functional vs OOP — Same Problem, Both Ways posts/getman: Getman — Declarative API Tester for CLI & TUI posts/how-compilers-optimize: How Compilers Actually Optimize Your Code posts/http3-quic: HTTP/3 and QUIC — Why They Matter posts/leetcode-vs-engineering: LeetCode vs Real Engineering Skills posts/llm-from-scratch: LLM from Scratch — GPT-Style Transformer in PyTorch posts/lsm-trees-bloom-filters: LSM Trees & Bloom Filters — Production Deep Dive posts/mcp-workflow-builder: MCP Workflow Builder — Visual DAG for MCP Tools posts/persistent-memory: Persistent Memory — Long-Term Memory for AI Agents via MCP posts/playcli: PlayCLI — Terminal Video Player posts/postgres-mvcc: How PostgreSQL MVCC Works — Multi-Version Concurrency Control Deep Dive posts/raft-consensus: Raft Consensus Algorithm Explained posts/rust-borrow-checker: Rust Borrow Checker — Catches Real Bugs posts/titan: Titan — Terminal AI Coding Agent posts/what-happens-url: What Happens Between Typing a URL and Seeing the Page posts/what-happens-when-you-run-a-program: What Actually Happens When You Run a Program posts/zero-knowledge-proofs: Zero-Knowledge Proofs Explained Simply webtui/components/accordion: Accordion webtui/components/badge: Badge webtui/components/button: Button webtui/components/checkbox: Checkbox webtui/components/dialog: Dialog webtui/components/input: Input webtui/components/popover: Popover webtui/components/pre: Pre webtui/components/progress: Progress webtui/components/radio: Radio webtui/components/range: Range webtui/components/separator: Separator webtui/components/spinner: Spinner webtui/components/switch: Switch webtui/components/table: Table webtui/components/textarea: Textarea webtui/components/tooltip: Popover webtui/components/typography: Typography webtui/components/view: View webtui/contributing/contributing: Contributing webtui/contributing/contributing: ## Local Development webtui/contributing/contributing: ## Issues webtui/contributing/contributing: ## Pull Requests webtui/contributing/style-guide: Style Guide webtui/contributing/style-guide: ## CSS Units webtui/contributing/style-guide: ## Selectors webtui/contributing/style-guide: ## Documentation webtui/installation/astro: Astro webtui/installation/astro: ## Scoping webtui/installation/astro: ### Frontmatter Imports webtui/installation/astro: ### ‹style› tag webtui/installation/astro: ### Full Library Import webtui/installation/nextjs: Next.js webtui/installation/vite: Vite webtui/plugins/plugin-dev: Developing Plugins webtui/plugins/plugin-dev: ### Style Layers webtui/plugins/plugin-nf: Nerd Font Plugin webtui/plugins/theme-catppuccin: Catppuccin Theme webtui/plugins/theme-custom: Custom Theme webtui/plugins/theme-everforest: Everforest Theme webtui/plugins/theme-gruvbox: Gruvbox Theme webtui/plugins/theme-nord: Nord Theme webtui/plugins/theme-vitesse: Vitesse Theme webtui/start/ascii-boxes: ASCII Boxes webtui/start/changelog: Changelog webtui/start/installation: Installation webtui/start/installation: ## Installation webtui/start/installation: ## Using CSS webtui/start/installation: ## Using ESM webtui/start/installation: ## Using a CDN webtui/start/installation: ## Full Library Import webtui/start/installation: ### CSS webtui/start/installation: ### ESM webtui/start/installation: ### CDN webtui/start/intro: Introduction webtui/start/intro: ## Features webtui/start/plugins: Plugins webtui/start/plugins: ## Official Plugins webtui/start/plugins: ### Themes webtui/start/plugins: ## Community Plugins webtui/start/theming: Theming webtui/start/theming: ## CSS Variables webtui/start/theming: ### Font Styles webtui/start/theming: ### Colors webtui/start/theming: ### Light & Dark webtui/start/theming: ## Theme Plugins webtui/start/theming: ### Using Multiple Theme Accents webtui/start/tuis-vs-guis: TUIs vs GUIs webtui/start/tuis-vs-guis: ## Monospace Fonts webtui/start/tuis-vs-guis: ## Character Cells posts/termium: Termium — Chromium-Based Terminal Browser posts/new-projects-july-2026: New Projects — GRIT, Sage, Terrain Analyser, Self-Healing RAG, and More watchlist: 🎬 Watchlist posts/graph-engineering-rag: Graph Engineering replaced RAG at Microsoft, Stanford and Anthropic posts/graph-engineering-claude: Graph Engineering with Claude: 14-Step Roadmap from 0 to Graph Architect posts/agent-harness-loop-graph-engineering: Agent Harness Engineering vs Loop Engineering vs Graph Engineering
 Theme Current: Light j/k or ↑/↓ + Enter

What Happens Between Typing a URL and Seeing the Page

A deep dive into the journey of a single URL — URL parsing, DNS resolution, TCP/TLS handshakes, HTTP requests, server processing, and browser rendering — with a full timing breakdown.

🎯 The Question Every Interviewer Loves

“What happens when you type https://example.com into your browser and press Enter?”

This question endures because it touches every layer of the networking stack — application, transport, network, link, and physical. A complete answer touches on 8 distinct phases. Here they are, from keystroke to pixels.


1️⃣ URL Parsing and HSTS Check

Before anything leaves your machine, the browser parses the URL into its components:

ComponentValue
Protocolhttps
Hostnameexample.com
Port443 (implied by HTTPS)
Path/ (default)
Fragmentnone

The browser then checks its HSTS (HTTP Strict Transport Security) preload list. If the domain is found, the browser refuses to make an insecure HTTP connection — it upgrades directly to HTTPS. This prevents SSL-stripping attacks even on the first request.

If the domain is not preloaded, the browser sends a plain HTTP request first and relies on the server’s Strict-Transport-Security response header to remember the upgrade for future visits.


2️⃣ DNS Resolution

The browser needs the server’s IP address. It checks a chain of caches:

Browser cache → OS cache → Router cache → ISP DNS → Recursive resolver

If none of the caches have the mapping, the recursive resolver (usually your ISP’s or Cloudflare’s 1.1.1.1) begins the lookup:

  1. Root nameserver — tells the resolver where the .com TLD nameservers are
  2. TLD nameserver — tells the resolver where example.com’s authoritative nameservers are
  3. Authoritative nameserver — returns the actual A (IPv4) or AAAA (IPv6) record
Browser          Resolver         Root (.com)    TLD Authoritative
  │                  │               │            │
  │── query: ──────► │               │            │
  │  example.com     │── where's ──► │            │
  │                  │   .com?       │            │
  │                  │◄── go ask ────│            │
  │                  │   TLD →      │            │
  │                  │── where's ──────────────► │
  │                  │   example?   │            │
  │                  │◄────────── 93.184.216.34 ─│
  │◄── 93.184.216.34─│               │            │

Typical timing: 20–50ms for a cold cache, <1ms for a warm cache.

🔧 Modern DNS Optimizations

  • DNS prefetching: Browsers eagerly resolve domain names found in <a> tags before the user clicks
  • DNS-over-HTTPS (DoH): Encrypts DNS queries to prevent snooping (used by Firefox, Chrome)
  • DNS-over-TLS (DoT): Alternative encrypted DNS, preferred by Android

3️⃣ TCP Handshake

With the IP address known, the browser opens a TCP connection to port 443. TCP provides reliable, ordered delivery over an unreliable IP network. The three-way handshake establishes a connection:

Client                      Server
  │                           │
  │────── SYN ──────────────► │  Client: "Hey, let's talk"
  │                           │
  │◄───── SYN-ACK ────────── │  Server: "OK, I'm ready. You?"
  │                           │
  │────── ACK ──────────────► │  Client: "Confirmed."
  │                           │

Each unacknowledged segment triggers a retransmission, and the round-trip time (RTT) determines the minimum latency. On a 30ms RTT connection, the TCP handshake alone takes 60ms (one round trip for SYN → SYN-ACK, then ACK is the second).

TCP Fast Open (TFO) eliminates this on repeat connections by sending data inside the SYN packet, saving one RTT. Adoption is limited (~10% of servers).


4️⃣ TLS 1.3 Handshake

Plain TCP gets you a raw pipe, but you need encryption. TLS 1.3 (RFC 8446) reduced the handshake from 2 RTTs (TLS 1.2) to 1 RTT for fresh connections and 0 RTT for resumed ones.

Client                      Server
  │                           │
  │── ClientHello ──────────► │  Key share, supported ciphers, random nonce
  │   (public key share)     │
  │                           │
  │◄── ServerHello ──────────│  Cipher choice, server cert + signature
  │    + Cert + Finished     │
  │                           │
  │── Finished ─────────────►│  Client verifies cert, sends Finished
  │                           │
  │◄════ Encrypted Data ═══► │  Application data begins immediately

Certificate Verification

The browser checks:

  1. Certificate chain — Is the cert signed by a trusted CA?
  2. Hostname match — Does the cert’s Common Name or SAN match example.com?
  3. Expiry — Is the cert still valid?
  4. Revocation — Is the cert revoked? (via OCSP stapling, not CRL — OCSP is faster)

Typical timing: 1–2 RTT + certificate validation (~50–200ms total). With TLS 1.3 0-RTT resumption, returning visitors skip the handshake entirely.


5️⃣ HTTP Request

With a secure channel established, the browser sends an HTTP request:

GET / HTTP/1.1
Host: example.com
Accept: text/html,application/xhtml+xml,...
Accept-Encoding: gzip, deflate, br
Connection: keep-alive

HTTP/1.1 vs HTTP/2 vs HTTP/3

FeatureHTTP/1.1HTTP/2HTTP/3
TransportTCPTCPQUIC (UDP)
Multiplexing❌ (6 connections)✅ (streams)✅ (streams)
HOL blockingRequest-levelTCP-level❌ None
Header compressionHPACKQPACK
Server push
Connection migration
0-RTT

HTTP/1.1: One request per connection, limited to ~6 parallel connections per domain. Head-of-line blocking means one slow response blocks everything behind it.

HTTP/2: Multiplexes multiple streams over a single TCP connection. But TCP-level HOL blocking remains — if a single TCP packet is lost, all streams wait for the retransmission.

HTTP/3: Built on QUIC (RFC 9000), which runs over UDP. Each stream is truly independent. A lost packet only blocks its own stream. Connection migration lets you switch from Wi-Fi to cellular without dropping the connection.

Adoption as of mid-2026: HTTP/2 ~65%, HTTP/3 ~35%, HTTP/1.1 ~5% (mostly legacy).


6️⃣ Server Processing

The request arrives at the server. In a modern architecture, it passes through multiple layers:

  Client


  CDN (Cloudflare / Fastly / Akamai)
    │  ┌─ Cached static assets served here

  Load Balancer (Nginx / HAProxy / AWS ALB)
    │  ┌─ TLS termination, routing, health checks

  Application Server (Nginx + uWSGI / Node.js / FastAPI)
    │  ┌─ Session middleware, auth, business logic

  Application Code
    │  ┌─ Template rendering, API calls, auth checks

  Database (PostgreSQL / Redis / Memcached)
    │  ┌─ Query execution, cache lookup

  Response (the other way back)

CDN Caching

If the CDN has a cached copy of the response (common for static assets like CSS, JS, images), the request never reaches the origin server. CDN cache hits typically take 5–20ms; cache misses add 200–500ms for origin processing.

Server-Side Timing Breakdown

PhaseTypical Time
TLS termination (LB)1–5ms
Request routing0.1–1ms
Auth/session lookup5–20ms
Application logic20–200ms
Database queries5–100ms
Response serialization1–10ms
Total server time50–500ms

7️⃣ Parsing HTML, CSS, and JavaScript

The browser receives the response — typically an HTML document. Parsing begins immediately, even before the full response arrives (streaming parser).

Bytes → Characters → Tokens → Nodes → DOM Tree

The Parse Pipeline

  1. HTML parsing — Converts bytes to DOM nodes. When the parser encounters <script> tags (without async or defer), parsing blocks until the script is fetched and executed.
  2. CSS parsing — CSS is render-blocking. The browser won’t render anything until the CSSOM is built. Inline CSS is free; external stylesheets block the first paint.
  3. JavaScript execution — JS can modify the DOM (via document.write) and the CSSOM (via CSSStyleSheet), so the parser must wait.

Preload Scanner

Modern browsers deploy a preload scanner — a secondary parser that skims the raw bytes for resource URLs (images, scripts, stylesheets, fonts) and kicks off fetches in parallel before the main parser reaches them. This is why putting <link rel="preload"> or <link rel="preconnect"> in the <head> can dramatically improve load times.


8️⃣ Rendering: DOM → CSSOM → Render Tree → Layout → Paint → Compositing

This is where the page actually appears on screen. The rendering pipeline runs in the browser’s main thread (the “critical rendering path”):

Input: DOM Tree + CSSOM


1. Render Tree Construction
  │  ┌─ Combine DOM + CSSOM
  │  └─ Include only visible elements (no display:none)

2. Layout (Reflow)
  │  ┌─ Calculate geometry: position, width, height
  │  └─ Every element's box model is computed

3. Paint (Rasterization)
  │  ┌─ Fill pixels: colors, borders, shadows, text
  │  └─ Layers are painted independently

4. Compositing
  │  ┌─ Combine painted layers into final image
  │  └─ GPU-accelerated via compositor thread

Output: Screen pixels

Layout Thrashing

Every time you read a layout property (offsetHeight, getBoundingClientRect()) and then write a style change in the same frame, the browser must recalculate layout synchronously. This is called “layout thrashing” and is a common cause of jank.

// Bad — forces synchronous layout twice
const height = element.offsetHeight;
element.style.height = `${height + 10}px`;
element.style.width = `${element.offsetWidth + 10}px`;

// Good — batch reads then writes
const height = element.offsetHeight;
const width = element.offsetWidth;
element.style.height = `${height + 10}px`;
element.style.width = `${width + 10}px`;

Compositor-Only Properties

Modern browsers can animate transform and opacity entirely on the compositor thread, bypassing layout and paint entirely. This is why will-change: transform is the go-to performance hint for animations.


📊 Full Timing Breakdown

Here’s where the milliseconds go for a typical page load with a cold cache:

PhaseTypical Time% of Total
DNS resolution20–50ms2–5%
TCP handshake30–100ms (1 RTT)3–10%
TLS 1.3 handshake30–100ms (1 RTT)3–10%
HTTP request (latency)30–100ms (1 RTT)3–10%
Server processing100–500ms15–50%
HTML parsing + CSSOM50–200ms5–20%
Render tree + layout30–100ms3–10%
Paint + composite16–50ms2–5%
Total500ms–2s100%

With a warm cache (repeat visit, CDN cache hit, TLS 0-RTT), the same page loads in 100–400ms.


💡 Key Takeaways

  1. DNS is the hidden bottleneck — a cold DNS lookup can add 50+ms that most users notice
  2. TLS 1.3 is a huge win — 1 RTT instead of 2, plus 0-RTT for repeat visitors
  3. HTTP/3 eliminates HOL blocking — UDP + QUIC means one lost packet doesn’t stall everything
  4. CDNs matter more than you think — moving content geographically closer to users cuts latency by 50–200ms
  5. The render pipeline is fragile — layout thrashing from bad JS can undo all the network gains
  6. Every millisecond counts — Amazon found that every 100ms of latency costs 1% in revenue

The beauty of this question is that there’s always more depth. You can drill into congestion control, into the V8 parser, into the GPU compositor. The layers keep unfolding.


📖 Series Navigation

 praneshnikhar.site / posts / what-happens-url · Top 1:1