Free lesson · Frontend System Design
What happens when you type a URL and hit enter?
This is the oldest interview question in the book, and that's exactly why it's worth taking seriously. Interviewers keep asking it because the answer is a fractal - you can spend thirty seconds on it or three hours, and where you choose to go deep tells them precisely what kind of engineer you are. A backend candidate dives into load balancers and database queries. A networking specialist goes deep on TCP congestion control. You're interviewing for frontend, so the interesting half of this question - the half most people rush past to get to the "fun" server stuff - is everything that happens after the bytes come back: how a stream of HTML becomes pixels on a screen.
But you can't skip the first half, and here's the instinct I want to correct right away. Most people treat the network portion as "the internet does some magic and then the page shows up." That magic is a sequence of round trips, and every single round trip is latency you can see in a waterfall chart. When the requirement says "works on slow global networks" - and in a frontend round it almost always does - these round trips are the thing you're fighting. So let's walk the whole journey from keystroke to first pixel, and I'll flag where a frontend engineer actually has leverage, because that's the part the question is secretly testing.
The journey, stage by stage
sequenceDiagram
participant B as Browser
participant D as DNS Resolver
participant S as Server
B->>B: 1. Parse the URL
B->>B: 2. Check caches (DNS, HTTP)
B->>D: 3. DNS lookup (if not cached)
D-->>B: IP address
B->>S: 4. TCP handshake (SYN, SYN-ACK, ACK)
B->>S: 5. TLS handshake (for HTTPS)
B->>S: 6. HTTP request
S-->>B: 7. HTTP response (HTML)
B->>B: 8. Parse HTML → begin rendering
This sequence diagram shows the request lifecycle between the browser, a DNS resolver, and the server. First the browser parses the URL. Second, it checks its caches for DNS and HTTP entries. Third, if not cached, it performs a DNS lookup and receives an IP address. Fourth, it completes a TCP handshake with the server using SYN, SYN-ACK, and ACK. Fifth, for HTTPS, it performs a TLS handshake. Sixth, it sends the HTTP request. Seventh, the server returns the HTML response. Eighth, the browser begins parsing the HTML and starts rendering.
Stage 1 - Parsing the URL
The browser breaks the URL into its parts: scheme (https), host (example.com), port (default 443 for HTTPS), path, query string, and fragment. The fragment - the #section part - never gets sent to the server; it's purely a client-side instruction about where to scroll or, in many single-page apps, which view to show. That detail matters later when we talk about routing.
Stage 2 - The caches you forgot existed
Before the browser touches the network, it asks: do I already have what I need? There are layers of cache here, and a strong answer names them. The HTTP cache might hold the whole page or its assets. The DNS cache (in the browser, then the OS, then the router) might hold the IP so we skip the lookup entirely. The service worker - if the site registered one - can intercept the request before it even leaves the browser and serve a cached or offline response. We have a whole lesson on each of these later; the point here is that the fastest network request is the one you never make, and caching is how a frontend engineer makes requests disappear.
Stage 3 - DNS resolution
If the browser doesn't know the IP, it asks a DNS resolver to translate example.com into an address like 93.184.216.34. This can cascade through a recursive resolver, root servers, TLD servers, and the domain's authoritative nameserver - but in practice it's almost always cached somewhere along the chain, so it resolves in milliseconds. Your leverage here is small but real: a dns-prefetch resource hint tells the browser to resolve a domain you know you'll need (an API host, a CDN) before you actually need it, hiding the lookup latency.
Stage 4 - The TCP handshake
Now the browser opens a connection. Classic TCP needs a three-way handshake - SYN, SYN-ACK, ACK - which is one full round trip before any data flows. On a connection with 200ms latency, that's 200ms spent before you've sent a single byte of your request. This is one reason HTTP/3 exists: it runs over QUIC, which folds the connection setup and encryption into fewer round trips. You don't control the protocol entirely, but knowing why HTTP/3 is faster (fewer round trips on setup) is exactly the kind of depth that lands in a round.
Stage 5 - The TLS handshake
For HTTPS - which is everything now - there's an additional negotiation to establish encryption: agreeing on a cipher, exchanging keys, validating the certificate. In TLS 1.3 this is faster than it used to be (one round trip, or zero on resumption), but it's still latency. The frontend-relevant takeaway: this is per-connection overhead, which is why reusing connections and consolidating requests onto fewer hosts matters, and why HTTP/2's multiplexing - many requests over one connection - was such a big deal.
Stage 6 & 7 - Request and response
Finally the browser sends the HTTP request: a method (GET), the path, and a pile of headers (cookies, Accept, User-Agent, cache validators like If-None-Match). The server does its work and responds with a status code, response headers (crucially, the caching headers that decide whether you'll skip the network next time), and the body - for a page load, the HTML.
Important: Notice how much happened before the server sent a single byte of HTML: a DNS lookup, a TCP handshake, and a TLS handshake - potentially three round trips of pure waiting on a high-latency connection. This is why "just make the server faster" misses the point on the frontend. A 50ms server response wrapped in 600ms of connection setup is a 650ms wait. The connection overhead is often the bigger enemy, and it's invisible if you only look at server timings.
Stage 8 - The handoff to rendering
The instant the first chunk of HTML arrives, the browser starts parsing it - it does not wait for the whole document. This is the boundary where the networking story ends and the rendering story begins, and it's the boundary the rest of this module lives on. Everything up to here was about getting the bytes. Everything after is about turning bytes into pixels, which is the next lesson.
Where the frontend engineer actually has leverage
Let me make the "what can I control" question concrete, because reciting the stages isn't the point - knowing where you can act is.
| Stage | Your leverage as a frontend engineer |
|---|---|
| DNS | dns-prefetch / preconnect hints for known third-party hosts |
| TCP + TLS | preconnect to warm up the connection early; consolidate hosts; benefit from HTTP/2 multiplexing |
| Request | Eliminate the request entirely via caching (HTTP cache, service worker) |
| Response | Set/honor cache headers; compress the payload; keep the critical HTML small |
| Handoff | Get meaningful HTML to the browser fast (SSR/streaming) so rendering can start sooner |
The two resource hints worth knowing cold are preconnect and dns-prefetch. preconnect tells the browser "open a full connection - DNS, TCP, and TLS - to this origin now, because I'll need it shortly." Used on your API host or font provider, it hides hundreds of milliseconds of setup behind work the browser is already doing. dns-prefetch is the lighter cousin: just resolve the name, don't open the connection. These are one-line additions that move real numbers, which is exactly why mentioning them signals you've optimized a real page.
graph TD
A[Connection cost is the hidden enemy] --> B[DNS lookup]
A --> C[TCP handshake: 1 RTT]
A --> D[TLS handshake: 1 RTT]
B --> E[Hide with dns-prefetch / preconnect]
C --> E
D --> E
E --> F[Fewer round trips before first byte<br/>= faster perceived load]
This diagram frames connection cost as the hidden enemy of load performance. It breaks into three contributors: the DNS lookup, the TCP handshake costing one round trip, and the TLS handshake costing another round trip. All three can be mitigated using resource hints like dns-prefetch and preconnect, which warm the connection early. The result is fewer round trips before the first byte arrives, which means a faster perceived load.
The HTTP versions, briefly
You don't need to lecture on protocol history, but you should be able to say one sentence about why each version matters, because it occasionally comes up and it pairs naturally with this question.
- HTTP/1.1 opens a connection per request (with keep-alive reuse), and suffers head-of-line blocking - requests queue behind each other on a connection. The era of "concatenate everything into one file" was a workaround for this.
- HTTP/2 multiplexes many requests over a single connection, so bundling everything into one giant file became less necessary. It also added header compression and server push (now largely deprecated).
- HTTP/3 runs over QUIC (built on UDP), which eliminates the TCP-level head-of-line blocking and cuts connection-setup round trips. It's faster to establish and more resilient on flaky mobile networks - which ties straight back to the "global slow networks" requirement.
Note: The reason this matters for your decisions: on HTTP/1.1 you bundle aggressively to dodge per-request overhead; on HTTP/2 and HTTP/3 the cost of many small requests drops, so finer-grained code splitting becomes viable. The transport layer quietly shapes your bundling strategy. We'll return to this in the build-optimization lessons.
How to actually answer the question in a round
The trap with "what happens when you type a URL" is that it's open-ended enough to ramble for ten minutes and say nothing sharp. Don't. Structure it, signal where you could go deep, and then steer toward your strength - which, in a frontend round, is the rendering half.
Interview: A clean answer sounds like: "At a high level - URL parsing, cache checks, DNS, then a TCP and TLS handshake, the HTTP request and response, and then the browser starts parsing the HTML. I can go deep on any layer, but since this is a frontend role, the part I find most interesting is the handoff: the moment the first HTML bytes arrive, the browser begins building the DOM, and how fast we get meaningful pixels depends on the critical rendering path. Want me to go there?" That answer demonstrates breadth across the whole stack, names where you'd go deep, and steers toward the frontend competency they're hiring for - all in four sentences.
The other thing that lands: connect a stage to a real optimization unprompted. "DNS - which is why I'd preconnect to our API origin." "TLS handshake - which is per-connection, so I care about connection reuse." Each of those is a tiny proof that you don't just know the stage exists, you know what to do about it.
Reading the journey in a real waterfall
All of this stops being abstract the moment you open the Network panel in your browser's DevTools and look at the timing breakdown of a single request. The stages you just learned are right there, labeled, with milliseconds attached - and being able to read that breakdown out loud is a concrete, demonstrable skill. A single request bar typically decomposes into:
- Queued / Stalled - the browser waited before it could even start (connection limits, higher-priority requests ahead of it).
- DNS Lookup - stage 3. Usually tiny when cached, occasionally a surprise when it's not.
- Initial Connection - the TCP handshake, stage 4.
- SSL / TLS - stage 5, the encryption negotiation.
- Waiting (TTFB) - Time To First Byte: you've sent the request and you're waiting for the server's first byte. This blends network round-trip time with server processing.
- Content Download - the response body streaming in.
When a page feels slow, this breakdown tells you which stage to blame, and the fix is completely different for each. A long DNS time points at an uncached third-party host - reach for dns-prefetch. A long Initial Connection or SSL time points at connection setup overhead - reach for preconnect or fewer origins. A long TTFB points at the server or the network distance - a CDN edge closer to the user helps. A long Content Download points at payload size - compression and smaller assets. The same symptom ("it's slow") has four different root causes, and the waterfall is how you tell them apart instead of guessing.
Tip: A subtle but high-value detail: redirects multiply this whole sequence. Every redirect is a complete extra round trip - DNS (maybe), connection, TLS, request, response - before the real request even starts. A chain of
http → https → www → finalcan stack three redirects, each adding latency, before a single byte of useful content moves. When auditing performance, chasing redirect chains is often a fast, cheap win that nobody thinks to look at. The same logic applies to chained API calls on the client: if request B can't start until request A returns its result, you've serialized two full round trips, and on a high-latency connection that waterfall is felt directly by the user. Flattening those chains - fetching in parallel, or having the server return the combined data in one response - is the client-side mirror of avoiding redirect chains.
Why "the network is the user's, not yours" changes everything
I want to close the loop on the framing from the very first lesson, because this is where it becomes concrete. On the backend, you can throw hardware at a slow server. On the frontend, you cannot upgrade the user's phone or their train-tunnel WiFi. The latency between their device and your server is a fact of physics and geography you don't control - light takes real time to cross an ocean, and a congested mobile tower drops packets no matter how good your code is.
So the entire game of frontend network performance is working around a hostile, variable connection you can't fix: cache aggressively so you skip it, warm connections early so the handshakes hide behind other work, push content to edge servers physically closer to users so the round trips are shorter, and keep payloads tiny so even a slow pipe drains fast. Every technique we cover in the performance module is a variation on one of those four moves, and all four exist because the network belongs to the user. Internalize that and the optimizations stop being a list to memorize - they become obvious responses to a constraint you understand.
What you should carry out of this lesson
Typing a URL kicks off a sequence of network round trips - DNS, TCP, TLS, request, response - and the hidden cost is almost always the connection setup, not the server's processing time. As a frontend engineer your leverage is concentrated in two places: making requests disappear (caching) and warming or consolidating connections (resource hints, HTTP/2 multiplexing, HTTP/3). Knowing where those levers are is the difference between describing the network and improving it.
But all of that was about getting the bytes to the browser. The moment they arrive, an entirely separate and equally deep process begins: turning a character stream into a visual page. That process - the critical rendering path - is where frontend performance is won or lost, and it's where we go next.