What happens when you hit a URL — Critical Rendering Path explained
Browser does DNS → TCP → TLS → HTTP, then parses HTML into the DOM. CSS builds the CSSOM (render-blocking). DOM + CSSOM → render tree → layout → paint → composite. Synchronous JS blocks the parser; defer/async unblock it.
When the user hits enter on a URL, the browser executes a long chain of work. The interview-friendly summary: Network → Parse → Render. The senior detail is what blocks what — that's where every perf win comes from.
Phase 1: Network.
- DNS lookup. Resolve the hostname. Cached in OS, browser, router. ~20–120ms cold.
- TCP handshake. 3-way SYN/SYN-ACK/ACK. ~100ms RTT.
- TLS handshake. Certificate exchange + key agreement. 1–2 RTT (TLS 1.3 reduced this).
- HTTP request. Send GET, wait for first byte (TTFB). Server processes, possibly hits a CDN or origin.
- Response. Bytes stream back. With HTTP/2 multiplexing, more resources can come over the same connection.
Phase 2: Parse.
- HTML parser turns bytes into the DOM tree incrementally as bytes arrive.
- When the parser hits a
<link rel="stylesheet">, it requests the CSS but continues parsing HTML in parallel. - CSS is parsed into the CSSOM (CSS Object Model). CSS is render-blocking — the browser won't paint anything until CSSOM is built (otherwise unstyled content would flash).
- When the parser hits a
<script>:
- Default: parser pauses, fetches and executes the script before continuing. Worst case for performance.
async: fetched in parallel; executes whenever it arrives, possibly mid-parse. Order not guaranteed.defer: fetched in parallel; executes after parsing is complete, in order. Best default for most scripts.type="module": defer-like by default.
Phase 3: Render.
- Render tree combines DOM + CSSOM, dropping invisible nodes (
display: none,<head>, etc.). - Layout (reflow). Compute geometry of every render-tree node.
- Paint. Fill in pixels into layer bitmaps.
- Composite. GPU assembles layers and shows the frame.
Render-blocking vs parser-blocking.
- Render-blocking = blocks the first paint. CSS by default; sync scripts; web fonts (FOIT).
- Parser-blocking = blocks DOM construction. Sync scripts in particular — async/defer fix this.
The optimizations the CRP teaches you.
- Inline critical CSS above the fold so the first paint doesn't wait for an external stylesheet.
<script defer>for app code; the parser keeps going.<link rel="preload">for late-discovered resources (font referenced from CSS, JS chunk).<link rel="preconnect">to third-party origins to skip DNS+TCP+TLS later.- Server push / 103 Early Hints to start asset fetches before the HTML response body arrives.
- HTTP/2 (or HTTP/3) for multiplexing — many requests over one connection.
- Brotli/Gzip to shrink what's on the wire.
- HTML streaming + early flush so the browser starts parsing before the server is done generating.
Where the milliseconds go (rough budget for a fast site on a fast network):
- DNS+TCP+TLS: 100–300ms (warmable with preconnect).
- TTFB: 100–500ms (cacheable with CDN edge).
- HTML download: 50–200ms.
- CSS download + parse: 100–300ms.
- JS download + parse + execute: 100–1000ms+ (the usual culprit).
- Layout + paint of first frame: 50–200ms.
Web Vitals map cleanly onto CRP.
- TTFB measures the network phase up to the first response byte.
- FCP (First Contentful Paint) — first non-empty paint, requires HTML parsed enough + CSSOM built + render done.
- LCP (Largest Contentful Paint) — bigger render, often gated on the hero image (preload + dimensions help).
- CLS — caused by content shifting after first paint (images without dimensions, late-loaded fonts swapping).
- INP — long tasks blocking the main thread after first paint (heavy JS).
Mental model in one line: the browser races to build DOM + CSSOM, the first paint waits for both, and JS controls how much of that race it slows down.
Code
Follow-up questions
- •Why is CSS render-blocking?
- •What's the difference between async and defer?
- •How does inlining critical CSS speed up FCP?
- •What does TTFB include?
Common mistakes
- •Putting scripts in <head> without async/defer — blocks DOM construction.
- •Treating CSS as non-blocking — it is, by default.
- •Loading too many third-party scripts synchronously — kills FCP.
- •Forgetting that web fonts can FOIT (block text rendering) without `font-display: swap`.
Performance considerations
- •First paint is gated on CSSOM — keep critical CSS small and inlined.
- •Streaming HTML + early flush gives the browser something to parse sooner.
- •Reduce JS on the critical path — every KB delays interactivity.
Edge cases
- •<link rel='preload'> without `as` — counted but downloaded again.
- •Service worker can intercept and serve from cache, short-circuiting most of phase 1.
- •HTTP/2 server push exists but is largely deprecated in favor of 103 Early Hints.
Real-world examples
- •Next.js streams HTML with React 18 to start CRP before the full page is generated.
- •Cloudflare Early Hints sends 103 with link preloads while the origin is still working.