It is the most famous interview question in web development, and it deserves better than a memorised list: what happens between pressing Enter on a URL and seeing the page? The honest answer is a relay race across half the disciplines of computing — naming, routing, cryptography, parsing, layout — each leg measured in milliseconds. Understanding the legs is what turns performance work from cargo culting into engineering.
Here is the race, run once, in order.
Finding the server: DNS, TCP, TLS
First the name must become an address. The browser asks the operating system, which asks a resolver, which — on a cache miss — walks the DNS hierarchy from root to top-level domain to the site's own nameservers, returning an IP address. Caches at every layer mean the full walk is rare; its result is why the first visit to a domain is slower than the second.
With an address in hand, the browser opens a TCP connection — the three-way handshake, one round trip — and then negotiates TLS, proving the server's identity via its certificate and agreeing on encryption keys, costing another round trip or two. This is why physical distance still matters on the modern web: every handshake is a trip to the server and back, and a server an ocean away charges you for each one. CDNs exist to move those round trips closer to your chair. Newer protocols (HTTP/3 over QUIC) fold parts of these handshakes together, but the shape is the same: naming, then connection, then trust.
The response arrives: parsing and the two trees
The browser now sends the HTTP request and receives HTML — usually compressed, streamed in chunks. Parsing begins immediately, not at the end of the download, and this is where the page's dependency graph unfolds: stylesheets discovered in the head are fetched (they block rendering, because painting without styles would flash unstyled content); scripts without defer/async block parsing itself, which is exactly why script placement folklore exists; images and fonts queue up behind them.
From the HTML the browser builds the DOM tree — the page's structure — and from the CSS the CSSOM — the rules that apply to it. Combined, they yield the render tree: only the visible elements, each with its final computed style. Layout then assigns every box its geometry, and paint fills in the pixels, often on the GPU, in layers so that later scrolling and animation can move cheaply. First paint is the moment the relay's baton crosses into the user's eyes — typically after two DNS-to-TLS handshakes, one HTML round trip, and the critical CSS.
Why this map pays rent
Every performance technique you have ever been told is a move on this map. Preconnect warms the handshakes early; CDNs shorten every round trip; compressing and streaming HTML gets parsing started sooner; defer keeps scripts from blocking the parser; inlining critical CSS shortens the path to first paint; caching skips whole legs of the race on repeat visits. None of it is magic — each trick removes a specific, nameable wait from the story above.
It also explains the mysteries. Why is the site fast locally and slow for users abroad? Round trips. Why did one synchronous analytics script freeze rendering? Parser blocking. Why does the second page load feel instant? Warm DNS, warm connection, warm cache. The next time a page feels slow, don't guess — ask which leg of the relay is dragging, measure it in the browser's network panel, and fix the leg rather than the vibe.
Why the very first DNS lookup for a domain is the slowest one
A DNS resolution for a domain never previously looked up has to traverse the full resolution hierarchy — a recursive resolver, the root servers, the top-level domain servers, and finally the domain's own authoritative name server — each hop adding its own round-trip latency, while every subsequent lookup for the same domain within that record's TTL window is served instantly from a cache at whichever layer stored it, whether the browser's own DNS cache, the operating system's, or the recursive resolver's. This is precisely why the very first request to a new third-party domain a page depends on tends to be disproportionately slow compared to every request afterward, and why techniques like DNS prefetching exist specifically to get that one slow first lookup out of the way before it would otherwise block something the user is actually waiting on.
TLS handshake: what actually happens during the padlock, and why TLS 1.3 made it faster
Establishing a secure connection requires exchanging cryptographic parameters before any actual application data can flow, and TLS 1.2 required two full round trips to complete this handshake, while TLS 1.3 reduced that to a single round trip by restructuring which cryptographic parameters the client can safely propose upfront rather than waiting for the server's response first — for a connection already established with a server recently, TLS 1.3 also supports resuming with zero additional round trips at all in many cases, using session parameters cached from the previous connection, which is a meaningful, measurable latency saving that requires no application code changes at all, only using a modern TLS version.
HTML parsing produces two trees, not one, before anything is visible
The browser builds a DOM tree from the parsed HTML and a separate CSSOM tree from all parsed stylesheets, and neither tree alone is sufficient to paint anything — the two are combined into a render tree containing only the nodes that will actually be visually rendered (a `display: none` element, for instance, exists in the DOM but is deliberately excluded from the render tree), and only once that combined render tree exists can the browser compute layout and finally paint pixels to the screen, which is the concrete, multi-step reason a page with either a very large DOM or a very large, unoptimized stylesheet delays first paint even after all the necessary bytes have already arrived over the network.
Why HTTP/2 and HTTP/3 changed the shape of this whole story for pages with many resources
Under HTTP/1.1, a browser could only make a limited number of simultaneous connections per domain, which meant a page requesting dozens of separate resources queued many of those requests behind each other rather than fetching them all in parallel — HTTP/2 introduced multiplexing, letting many requests and responses share a single connection concurrently without queuing behind one another, and HTTP/3, built on QUIC rather than TCP, went further by removing a specific problem called head-of-line blocking at the transport layer itself, where a single lost packet under HTTP/2's TCP-based multiplexing could stall every other in-flight request sharing that same connection, not just the one request the lost packet actually belonged to.
Why a CDN changes the very first step of this entire story
For a site served through a CDN, the DNS lookup covered earlier in this article resolves not to the origin server's own address but to whichever CDN edge location is geographically or network-topologically closest to the requesting client, which is precisely what makes a CDN effective: nearly every step described in this article — the TCP handshake, the TLS handshake, and often the actual response itself if the resource is cached at the edge — happens against a nearby edge server rather than the origin potentially located on the other side of the world, cutting the round-trip latency for every one of those steps correspondingly.
Why the browser's preload scanner starts fetching resources before the parser reaches them
Modern browsers run a separate, lightweight preload scanner alongside the main HTML parser specifically to look ahead in the document for resources — images, scripts, stylesheets — that will be needed soon, and begin fetching them in the background before the main parser has actually reached that point in the document, which is exactly why a resource referenced late in the HTML can sometimes already be loading by the time the parser gets there, rather than only starting its fetch at that later point the way a naive, single-pass parsing model would suggest.
Why the browser's connection pool limits are still relevant even under HTTP/2 multiplexing
Even with HTTP/2 allowing many requests to share one connection, browsers still cap how many separate connections they will open to distinct origins simultaneously, which is precisely why sharding assets across many different subdomains — once a common optimization technique under HTTP/1.1's much stricter per-domain connection limits — has become largely counterproductive under HTTP/2, since it now works against multiplexing's benefit rather than helping to route around an older limitation that no longer applies the same way.
Why the browser's main thread is the single resource every step in this story eventually competes for
Parsing HTML, computing styles, executing JavaScript, and painting pixels are, for the most part, all competing for time on the same single main thread, which is precisely why a long-running script anywhere in this pipeline delays every other step queued behind it — a page load is not truly a sequence of independent phases running in parallel, it is a single thread juggling all of them, and understanding that shared constraint is what makes sense of why an unrelated-looking JavaScript bug can visibly delay something as seemingly unconnected as the initial paint.
Why Service Workers add an entirely new layer to this whole story for repeat visits
A registered Service Worker sits between the page and the network, intercepting every request and deciding whether to serve it from a local cache, fetch it fresh, or some combination of both, which means a repeat visit to a page with an active Service Worker can skip much of the network story described throughout this article entirely — DNS, TCP, TLS, even the HTTP request itself — serving content directly from a local cache the Service Worker controls, which is the specific mechanism that makes offline-capable web applications and near-instant repeat loads possible.
Why HTTP/3's use of UDP rather than TCP required rethinking firewall and network assumptions
TCP has been the assumed transport layer underlying nearly all web traffic for decades, and QUIC, the protocol underlying HTTP/3, instead runs over UDP, which some older or overly restrictive firewalls and network middleboxes were never configured to expect for regular web traffic, occasionally blocking or degrading it — this is part of why HTTP/3 adoption has been gradual rather than an instant, universal switch, since it required not just browser and server support but real-world network infrastructure catching up to a genuinely different transport-layer assumption most of the internet's existing equipment was not originally built around.
Why understanding this whole sequence end to end is what makes performance debugging tractable
Every technique discussed elsewhere in this library's web-performance cluster — critical CSS, lazy loading, cache headers — targets one specific step in the sequence this article has walked through, and knowing the full sequence in order is what lets a real investigation be targeted rather than a scattershot attempt at every optimization technique available regardless of whether it actually addresses the specific step that is genuinely slow for a given page.