Episode 1 — Fundamentals / 1.2 — Client Server Architecture
1.2.d - What Happens When You Visit a Website
Big Picture: Typing a URL feels like one instant action, but it kicks off a long chain: parsing, caches, DNS, TCP, TLS, HTTP, server work, bytes on the wire, and finally the browser’s parsing, layout, paint, and compositing pipelines — plus many more requests for subresources. This lesson is the end-to-end map of that journey.
Navigation: ← 1.2.c — How HTTP Request and Response Cycle Works · 1.2.e — Front-end vs Back-end →
How to Read This Lesson
This document walks one primary navigation (first HTML document) in roughly chronological order. Real pages parallelize steps (HTTP/2 multiplexing, preconnect, speculative prefetch), but mentally you still reason about each phase the same way.
1. You Type the URL
1.1 What the browser receives
When you submit the address bar (Enter) or click a link, the browser receives a string. That string is interpreted as a URL (or URI). The browser’s URL parser splits it into standard parts.
1.2 URL structure (parsing)
A typical HTTPS URL:
https://www.example.com:443/products?id=42&sort=price#reviews
| Part | Name | In the example | Role |
|---|---|---|---|
https:// | Scheme (protocol) | https | Chooses default port (443 for HTTPS, 80 for HTTP) and whether TLS is required. |
www.example.com | Host (hostname) | www.example.com | What must be resolved via DNS to an IP address (unless bypassed by cache or /etc/hosts). |
:443 | Port (optional) | 443 | Omitted in most URLs because it matches the scheme default. If present, it overrides the default. |
/products | Path | /products | Resource location on the server (how the origin interprets it is app-defined). |
?id=42&sort=price | Query string | id=42, sort=price | Key–value pairs for the server (and sometimes client-side scripts); starts with ?, pairs separated by &. |
#reviews | Fragment | reviews | Not sent to the server in the HTTP request. Used by the browser after the document loads (scroll-to-id, SPA routers, etc.). |
Parsing steps (conceptual):
- Identify scheme up to
://(or legacy forms likehttp:for some parsers). - Split authority (
userinfo@host:port) — web URLs usually have nouserinfo. - Split path from query at first
?. - Split fragment at first
#(fragment is stripped for the network fetch). - Percent-decode path and query components where rules allow (encoding matters for international domains and file names).
Normalization: Browsers may apply rules (lowercasing scheme/host for comparison, adding trailing slashes for directories on some navigations, etc.) before issuing a network request.
1.3 HSTS and the HSTS preload list
HSTS (HTTP Strict Transport Security) tells browsers: “only use HTTPS for this host for a period of time.”
Before the browser opens a plain HTTP connection, it checks:
- In-memory HSTS cache for the hostname (learned from past
Strict-Transport-Securityheaders). - The HSTS preload list shipped inside the browser (a curated list of domains committed to HTTPS-only).
If either says “HTTPS only,” the browser will upgrade http:// to https:// (or block mixed/insecure cases per policy) before network connection setup.
Why it matters: It closes the window where an attacker could downgrade you to unencrypted HTTP on untrusted networks.
2. Browser Cache Check
Before DNS and new sockets, the browser asks: “Do I already have a usable response for this URL?”
2.1 Memory cache (fast)
- Where: RAM inside the browser process (per profile / partition).
- What: Recently fetched responses (HTML, images, scripts, etc.) keyed by URL + credentials mode + other cache keys.
- Rules: Honors Cache-Control, Expires, ETag, Last-Modified, Vary, and request headers. May return 304 Not Modified after revalidation.
Hit path: If a fresh enough entry exists, the browser may skip the network entirely for that resource (or send a conditional request).
2.2 Disk cache (persistent)
- Where: On-disk cache database (still private to the browser).
- What: Larger, slower than memory, survives restarts.
- Role: Same HTTP caching semantics, different storage medium.
Typical order: Check memory → if miss or needs revalidation, consult disk → if still miss, go to network.
2.3 Service Worker cache
If a Service Worker is registered and in control of the scope:
- The
fetchevent may intercept the request. - The worker can respond from
caches.match(), the network, or a synthesized response.
Important: Service Workers can change effective caching behavior (offline-first apps, stale-while-revalidate, etc.) even when HTTP headers would otherwise allow network fetches.
2.4 What happens on a full cache miss
If no acceptable cached representation exists (or policy forbids cache use), the browser proceeds to DNS resolution (if the host is not already known) and then connection establishment.
3. DNS Resolution (Full Hierarchy)
DNS maps a hostname (www.example.com) to one or more IP addresses (e.g., 93.184.216.34 for IPv4, or an IPv6 AAAA record).
3.1 Resolver chain (what runs on your machine first)
┌─────────────────────────────────────────────────────────────────────────┐
│ YOUR DEVICE │
│ │
│ 1) Browser DNS cache (if implemented / short TTL memory) │
│ │ miss │
│ ▼ │
│ 2) OS resolver cache (system stub resolver) │
│ │ miss │
│ ▼ │
│ 3) /etc/hosts (Unix/macOS) or C:\Windows\System32\drivers\etc\hosts │
│ │ no static entry │
│ ▼ │
│ 4) Stub resolver sends query to configured "recursive resolver" │
│ (often your ISP, Google 8.8.8.8, Cloudflare 1.1.1.1, etc.) │
└─────────────────────────────────────────────────────────────────────────┘
/etc/hosts: Static file; developers use it to point domains to local IPs (127.0.0.1 myapp.test). If matched, no public DNS is queried for that name.
3.2 Recursive resolver (the “phone a friend who knows everyone”)
Your laptop usually does not walk the DNS tree itself. It asks a recursive resolver to do the full resolution and return a final answer.
The recursive resolver:
- Checks its own cache (hot records with TTL).
- If miss: performs iterative queries to authoritative servers, starting from hints.
3.3 DNS hierarchy (iterative resolution)
Goal: Find who is authoritative for www.example.com’s address records.
ROOT SERVERS (.)
│
┌────────────────────┼────────────────────┐
▼ ▼ ▼
".com" ".net" ".org"
TLD servers TLD servers TLD servers
│
│ "Who serves example.com?"
▼
AUTHORITATIVE for example.com
(registrar / DNS host's nameservers)
│
│ "What is www.example.com?"
▼
ANSWER: A / AAAA records (IPs) + TTL
ASCII walk (conceptual messages):
[Your stub] ----Q: A? www.example.com-------------------> [Recursive resolver]
[Recursive] ---Q: NS? com. -----------------------------> [Root server]
[Root] -------A: "Ask these .com TLD servers..." --------> [Recursive]
[Recursive] ---Q: NS? example.com. ---------------------> [.com TLD server]
[.com TLD] ----A: "Authoritative NS for example.com..." -> [Recursive]
[Recursive] ---Q: A? www.example.com. ------------------> [example.com NS]
[Authoritative] A: 93.184.216.34 (TTL=300) ---------------> [Recursive]
[Recursive] ---A: 93.184.216.34 --------------------------> [Your stub/browser]
Record types you will see in practice:
| Record | Meaning |
|---|---|
| A | IPv4 address |
| AAAA | IPv6 address |
| CNAME | Alias to another hostname (another lookup chain) |
| NS | Which server is authoritative for a zone |
TTL: Seconds to cache an answer. Low TTL enables faster failover; high TTL reduces DNS load.
3.4 DNS timing note
DNS is often cached at multiple layers, so subsequent visits may add ~0 ms of DNS latency. Cold lookups dominate the 20–100 ms range on good networks (higher on mobile or distant resolvers).
4. TCP Connection (Three-Way Handshake)
TCP provides reliable, ordered byte streams between two endpoints (your IP:port ↔ server IP:port).
Before HTTP or TLS application data, TCP establishes state with a three-way handshake.
4.1 Handshake overview
CLIENT SERVER
│ │
│ ① SYN │
│ seq=x (client ISN) │
│ ─────────────────────────────────────────────────► │
│ │
│ ② SYN-ACK │
│ seq=y, ack=x+1 │
│ ◄───────────────────────────────────────────────── │
│ │
│ ③ ACK │
│ seq=x+1, ack=y+1 │
│ ─────────────────────────────────────────────────► │
│ │
│ TCP connection ESTABLISHED │
│ (bidirectional data can now flow) │
▼ ▼
Packet-by-packet (conceptual fields):
① SYN
• Flags: SYN=1, ACK=0
• Seq: random Initial Sequence Number (ISN) = x
• Purpose: "I want to open a connection; I start counting bytes at x."
② SYN-ACK
• Flags: SYN=1, ACK=1
• Seq: server ISN = y
• Ack: x + 1 (expects client's next byte)
• Purpose: "I accept; my ISN is y; I saw your SYN."
③ ACK
• Flags: SYN=0, ACK=1
• Seq: x + 1
• Ack: y + 1
• Purpose: "I acknowledge your SYN; connection is up."
Why three steps? Both sides must agree on sequence numbers and prove bidirectional reachability before transferring application payloads.
Ports: Browser picks an ephemeral local port; destination is usually 443 (HTTPS) or 80 (HTTP).
5. TLS Handshake (HTTPS)
After TCP is up, HTTPS wraps HTTP inside TLS (historically “TLS on top of TCP”; HTTP/3 uses QUIC instead — this section is the common TLS 1.2/1.3 over TCP path).
5.1 Goals of TLS
- Authenticate the server (and optionally the client).
- Negotiate cipher suites and protocol version.
- Establish shared symmetric keys for bulk encryption.
- Ensure integrity (tampering detected).
5.2 Simplified TLS 1.3 flow (modern browsers)
CLIENT SERVER
│ │
│ ClientHello │
│ • supported versions, groups, sig algs │
│ • key share (e.g., ECDHE) │
│ • SNI: server name (which certificate) │
│ ───────────────────────────────────────────────────► │
│ │
│ ServerHello │
│ • chosen version / cipher │
│ • server key share │
│ Certificate (chain) │
│ CertificateVerify (proves private key) │
│ Finished (MAC over handshake) │
│ ◄────────────────────────────────────────────────── │
│ │
│ Finished │
│ ───────────────────────────────────────────────────► │
│ │
│ ===== Symmetric keys ready ===== │
│ HTTP request / response now encrypted (AEAD) │
▼ ▼
5.3 Certificate verification (client side)
The browser:
- Builds a chain from server cert → intermediate(s) → trust anchor in OS/browser store.
- Checks hostname against Subject Alternative Name (SAN) (matches SNI / URL host).
- Validates notBefore / notAfter (expiry).
- Checks revocation signals where applicable (OCSP stapling, CRL policies — varies by client).
- Applies CT (Certificate Transparency) and pinning policies where configured.
If any critical check fails: connection aborted; user sees a certificate error.
5.4 Key exchange and symmetric encryption
- Asymmetric crypto (certificates, signatures) bootstraps trust.
- Ephemeral Diffie–Hellman (e.g., ECDHE) lets both sides derive the same session keys without transmitting them in the clear.
- Bulk data uses fast symmetric ciphers (e.g., AES-GCM) with authenticated encryption.
TLS 1.3 often completes in 1-RTT (one round trip after TCP) or 0-RTT resumption in special cases (tradeoffs with replay risk for early data).
Typical timing: 50–200 ms added on top of TCP for full handshake; session resumption lowers this.
6. HTTP Request (Browser Constructs and Sends)
Once TLS (or plain TCP for HTTP) is ready, the browser sends an HTTP request for the document (often GET).
6.1 Request line + headers (HTTP/1.1 style)
Even on HTTP/2/3, the semantic request is the same: method, authority, path, headers, optional body.
6.2 Example raw HTTP/1.1 request
GET /products?id=42&sort=price HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.9
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cookie: sessionid=abc123; theme=dark
Upgrade-Insecure-Requests: 1
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Header roles (the ones you asked for + context):
| Header | Role |
|---|---|
| Host | Required in HTTP/1.1; tells server which virtual host (same IP, many sites). |
| User-Agent | Client fingerprint; servers use it for analytics or (fragile) content negotiation. |
| Accept | What content types the client prefers for the response. |
| Accept-Encoding | Compression algorithms the client can decode (gzip, br (Brotli), etc.). |
| Connection | keep-alive reuses TCP for follow-up requests (HTTP/1.1 default; HTTP/2 multiplexes differently). |
| Cookie | Stateful cookies previously set by this origin (Set-Cookie). |
Note: With HTTP/2 or HTTP/3, these become HPACK/QPACK-encoded fields, not literal CRLF lines — but the meaning is identical.
7. Server Processing (Typical Production Path)
The request hits infrastructure long before your app function runs.
7.1 Load balancer
A load balancer (hardware or cloud LB) accepts connections on public IPs:
- Terminates TLS at the edge (common) or passes through encrypted traffic.
- Chooses a healthy backend via algorithms (round-robin, least connections, geographic, etc.).
- May inject headers (
X-Forwarded-For,X-Forwarded-Proto) so apps know the original client IP and scheme.
7.2 Reverse proxy (e.g., Nginx)
Nginx often sits in front of app servers:
- Serves static files directly from disk (fast).
- Applies rate limits, TLS, gzip/brotli, HTTP/2 termination.
- Routes paths to upstreams:
location /api/→ Node cluster;/→ static or app.
Internet ──► LB ──► Nginx (reverse proxy) ──► App server pool
7.3 Application server (Node.js, Python, etc.)
The app process:
- Parses the HTTP request (framework middleware).
- Authenticates session/JWT; authorizes the route.
- Runs handlers (controller → services).
- May call databases, caches (Redis), queues, other microservices.
- Renders a template or returns JSON.
7.4 Database queries (when needed)
For dynamic HTML:
- ORM/query builds SQL/NoSQL operations.
- Indexes and connection pools dominate latency.
- Results feed HTML generation (SSR) or JSON for client rendering (CSR) — first navigation might still be HTML shell.
7.5 Response generation
The stack assembles status, headers (Content-Type, Set-Cookie, caching, security headers), and body bytes (HTML, error page, redirect).
8. HTTP Response
8.1 Example raw HTTP/1.1 response
HTTP/1.1 200 OK
Date: Sat, 11 Apr 2026 18:00:00 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 2145
Cache-Control: max-age=3600
Set-Cookie: sessionid=xyz789; Path=/; HttpOnly; Secure; SameSite=Lax
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Content-Type-Options: nosniff
Content-Encoding: gzip
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example Store</title>
<link rel="stylesheet" href="/assets/app.css">
</head>
<body>
<h1>Products</h1>
<script src="/assets/app.js"></script>
</body>
</html>
(In practice, binary gzip bytes follow the header block; shown conceptually.)
Key pieces:
| Piece | Meaning |
|---|---|
| Status line | 200 OK = success; 301/302 redirects; 404 not found; 500 server error. |
| Content-Type | Tells the browser how to interpret bytes (text/html, application/json, …). |
| Set-Cookie | Stores cookies for future requests to this host (with security attributes). |
| Caching headers | Control browser/CDN caching (Cache-Control, ETag, …). |
The browser receives bytes incrementally; for HTML it can often start parsing before the full download completes.
9. Browser Rendering Pipeline (Very Detailed)
The rendering engine turns HTML/CSS/JS into pixels. Chrome’s pipeline is split into teams (Blink, V8, Skia, GPU process). The logical stages below match how engineers reason about performance.
9.1 HTML parsing → DOM tree
Input: HTML byte stream (UTF-8 typically).
Steps:
- Decode bytes to characters (encoding sniffing if needed).
- Tokenize: scanner emits tokens (
StartTag: html,Characters: "Hello", …). - Tree construction: builds the Document Object Model (DOM) — a tree of nodes (
Element,Text,Comment, …). - Error tolerance: HTML is parsed with recovery rules (unclosed tags, mis-nested markup); unlike XML, browsers try to keep going.
Special tags alter parsing:
<script>: pauses HTML parsing for classic parser-inserted scripts (fetches + executes unlessasync/defer/modulerules apply).<link rel="stylesheet">: does not pause parsing in the same way as scripts, but rendering is blocked until CSS is known (see below).
Output: DOM tree — the live structure exposed to JavaScript via document.
9.2 CSS parsing → CSSOM
Input: CSS from <style>, external stylesheets, user-agent defaults.
Steps:
- Parse into tokens and rules.
- Build the CSS Object Model (CSSOM) — a tree of style rules with selectors and declarations.
- Cascade: origin (user agent, author, user), importance (
!important), specificity, source order.
Output: CSSOM — how styles apply to (hypothetical) elements.
9.3 JavaScript execution (blocks rendering in classic cases)
V8 (in Chrome) compiles and runs JS.
Critical facts:
- Synchronous classic scripts in the middle of HTML block HTML parsing while downloaded and executed.
- Document CSS is not fully applied for first paint until render-blocking stylesheets are loaded and parsed (unless media queries or other optimizations exclude them).
async/defer/type="module"change when scripts run relative to parsing.
DOM APIs can mutate the DOM; mutations may schedule style recalc and layout.
9.4 Render tree (DOM + CSSOM)
The engine combines DOM + CSSOM into a render tree (not the same as DOM: e.g., head and display:none subtrees often omitted; pseudo-elements appear).
Used for: determining what needs visual representation.
9.5 Layout (reflow)
Layout computes geometry: box model, position, line breaks, flex/grid sizing, viewport coordinates.
Triggers: DOM changes affecting geometry, font loads, viewport resize, reading certain layout properties after writes (layout thrash if alternating read/write).
Output: layout tree with positions and sizes.
9.6 Paint
Paint turns boxes into draw instructions: text, colors, borders, shadows, images.
Output: display lists / paint ops (implementation detail), often split into layers.
9.7 Compositing
Compositing merges layers on the GPU when possible (transform/opacity animations, promoted layers).
Why: scrolling and animations can avoid repainting the entire page by moving layers.
9.8 End-to-end rendering mental model
HTML bytes ──► DOM ──┐
├──► Render tree ──► Layout ──► Paint ──► Composite ──► Pixels
CSS bytes ──► CSSOM ┘
▲
└── JS can mutate DOM/CSSOM; triggers re-layout/re-paint as needed
10. Subresource Loading
The initial HTML almost always references more URLs:
- CSS:
<link rel="stylesheet"> - JS:
<script src="..."> - Images:
<img src>,srcset, CSSbackground-image - Fonts:
@font-facein CSS
Each URL typically triggers another HTTP request (same TCP connection reused on HTTP/1.1 keep-alive, or multiplexed on HTTP/2+).
Discovery:
- HTML parser finds tags and schedules fetches.
- Preload scanners may speculatively discover resources before the main parser reaches them.
Priorities: Browsers assign resource priorities (document > CSS > font > image, nuanced by fetchpriority, lazy loading, etc.).
CORS: Some resources (fonts, fetch, canvas) require Cross-Origin Resource Sharing checks.
11. Page Interactive: Lifecycle Events
11.1 DOMContentLoaded
Fires when HTML is fully parsed and DOM is built (without waiting for stylesheets, images, and async deferred subresources in the classic definition — nuances exist with styles blocking scripts).
Useful for: DOM-only initialization.
11.2 window.onload (load)
Fires when the page and its subresources (images, etc.) have loaded (roughly: loading spinner time).
Useful for: metrics that include full asset readiness (analytics).
Modern note: Single-page apps often add framework-specific “hydrated” or “ready” milestones beyond these events.
12. Complete ASCII Timeline (Approximate Timings)
Cold navigation, rough RTT-dominated ranges (varies wildly by geography, device, HTTP version, TLS resumption, CDN, cache):
0 ms User presses Enter; URL parsed; HSTS checked
│
~0-5 ms Cache checks (memory → disk → SW); possible instant return
│
~20-100 ms DNS (if uncached): stub → recursive → authoritative chain
│
~20-100 ms TCP handshake (1 RTT each leg; combined ~1.5 RTT typical)
│
~50-200 ms TLS full handshake (often 1-2 RTT; 0-RTT resumption faster)
│
~50-500 ms Server: LB → proxy → app → DB → HTML generation + TTFB
│
~100-500 ms Download HTML + parse + style + layout + paint + composite
│
parallel Subresources (CSS/JS/images/fonts): more DNS/TCP/TLS/HTTP,
overlapping on HTTP/2/3; extends "fully loaded" to seconds on heavy sites
▼
DOMContentLoaded (earlier) ····· window.onload (later, after many assets)
Throughput vs latency: Large pages may be bandwidth-bound; interactive apps may be CPU-bound during parse/JS.
Master Diagram: URL → Pixels
┌──────────────────────────────────────────────────────────────────────────────────┐
│ YOU TYPE / CLICK URL │
└──────────────────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────┐ miss ┌──────────────┐ miss ┌─────────────────────────┐
│ URL parse │────────────►│ Browser cache │────────────►│ DNS: hosts → recursive │
│ HSTS policy │ │ (mem/disk/SW) │ │ → root/TLD/auth │
└──────────────┘ └──────────────┘ └───────────┬─────────────┘
│ │ hit │
│ ▼ │ IP address
│ return cached │
│ representation │
│ ▼
│ ┌─────────────────────┐
│ │ TCP 3-way handshake │
│ └──────────┬──────────┘
│ │
│ ┌──────────▼──────────┐
│ │ TLS (HTTPS) │
│ │ certs + key agree │
│ └──────────┬──────────┘
│ │
│ ┌──────────▼──────────┐
│ │ HTTP GET document │
│ └──────────┬──────────┘
│ │
│ ┌───────────────────────────────────────────────────────┘
│ │
│ ▼
│ ┌─────────────┐ ┌──────────────┐ ┌────────────────────────────┐
│ │ Load │──►│ Reverse │──►│ App + DB + templates/API │
│ │ balancer │ │ proxy (e.g. │ │ → HTTP response (HTML) │
│ └─────────────┘ │ Nginx) │ └─────────────┬──────────────┘
│ └──────────────┘ │
│ ▼
│ ┌─────────────────────┐
└─────────────────────────────────────────────►│ Browser: DOM/CSSOM │
│ layout → paint → │
│ composite → pixels │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ Fetch subresources │
│ (CSS/JS/img/font) │
│ more HTTP cycles │
└──────────┬──────────┘
▼
┌─────────────────────┐
│ DOMContentLoaded / │
│ load · interactive UI│
└─────────────────────┘
Key Takeaways
- A URL is structured data: scheme, host, port, path, query, fragment — the fragment never hits the server.
- HSTS (including preload) can force HTTPS before any network I/O.
- Caching (memory, disk, Service Worker) can bypass DNS/TCP/TLS/HTTP entirely for repeat resources.
- DNS is hierarchical (root → TLD → authoritative); your machine usually trusts a recursive resolver to walk it.
- TCP establishes reliability; TLS adds authentication and encryption before HTTP bytes flow (on classic HTTPS).
- Production traffic flows through LB → reverse proxy → app → DB more often than “one bare server.”
- Rendering is pipelined: DOM + CSSOM → render tree → layout → paint → composite; JS and CSS timing matter for first paint and interactivity.
- One navigation spawns many requests; HTTP/2 and HTTP/3 multiplex them, but each resource still has a full logical lifecycle.
Explain-It Challenge
Without peeking back, explain aloud:
- Why the
#fragmentis not sent in the HTTP request, and one example of what still uses it in the browser. - The difference between a recursive DNS resolver and an authoritative nameserver.
- Three packets in order for TCP connection setup and what each acknowledges.
- Two distinct jobs TLS does that plain TCP does not.
- Why a
<script>withoutasync/deferin the middle of HTML can delay DOM construction, and how CSS can delay first paint even when parsing continues. - Walk DOMContentLoaded vs
loadin terms of what assets each waits for.
If you can explain those clearly to a peer, you have a senior-intern-grade mental model of the web stack.
Navigation: ← 1.2.c — How HTTP Request and Response Cycle Works · 1.2.e — Front-end vs Back-end →