From URL to pixels
Everything that happens between pressing Enter and seeing a page: DNS, TCP, TLS, the HTTP exchange, redirects, parsing, and subresources, with where the time goes on a real phone, so you know which part to fix when a page is slow.
The problem: two seconds of white
Priya is on a train, holding a three-year-old Android phone. A friend sends her a link to a recipe, and she taps it. The browser opens, the address bar fills in, and then the screen stays white for two long seconds.
At about 2.8 seconds the title and the first paragraph appear. The big photo of the dish shows up at 4.2 seconds.
The recipe site's team is puzzled. On their laptops, on office Wi-Fi, the same page appears in 600 ms, and their server renders it in 200 ms. So where did more than two seconds of white go?
Most of it went to the network, before the browser had a single byte of HTML to work with. The phone had to find the server's address, open a connection, agree on encryption, follow a redirect to a second address and do some of that again, and only then ask for the page. Each of those steps is a message that travels to a server and back, and on a train every trip takes about 150 ms, often more.
The journey in one list
Here is everything that happens between tapping a link and seeing the page, in order. The rest of this lesson takes each step apart, with real numbers.
- Before the network. The browser decides whether the text is a URL or a search, upgrades
httptohttpsif it knows the site wants that, and checks whether a service worker or the HTTP cache can answer without the network. - DNS. It turns the name
recipes.exampleinto an IP address such as203.0.113.10. - TCP. It opens a reliable connection to that address (or, with HTTP/3, a QUIC connection over UDP).
- TLS. It agrees on encryption keys with the server and checks the server's certificate.
- HTTP. It sends the request (
GET /pasta HTTP/2with headers) and waits for the response. The first byte of the answer marks TTFB, the time to first byte. - Redirects. If the answer is
301or302, it goes back to step 2 or 3 for the new address. - Parsing and subresources. It reads the HTML as it streams in, finds the CSS, JavaScript, fonts, and images it names, and fetches them, often from other servers that need their own DNS, TCP, and TLS.
- Rendering. It builds the DOM and styles, lays out the page, and paints it. The first text or image on screen is FCP, the first contentful paint. The largest one, often the hero image, is LCP, the largest contentful paint.
Why a frontend engineer should care
It is tempting to treat steps 2 to 6 as "the backend's problem". But many of the choices that make them slow are frontend choices: how many different domains the page loads from, whether the link points at the final URL or one that redirects, whether the HTML is streamed or built all at once, and which files the browser can discover early.
The rendering half (parsing, render-blocking files, style, layout, and paint) has its own lesson, the critical rendering path. This lesson spends its depth on the network journey and on the timeline of a whole page load: what each phase costs, how to measure it, and what to change when one is too slow.
Think first
The page appears in 600 ms on office Wi-Fi and in 2.8 seconds on the train. The server time and the bytes are the same. What is most likely different?
The round-trip time. On office Wi-Fi to a nearby server, a message goes there and back in maybe 10 to 20 ms. On a phone on a moving train it is 150 ms or more. This page needs about 14 round trips before the first paint (a redirect, DNS, TCP, TLS, the request, then the same again for the CSS and the font on other servers), so the same steps cost many times more.
A slower phone CPU adds to it, but for the time before the first paint the network trips are usually the biggest part.
The mental model: a chain of round trips
Keep one picture in your head: a page load is a chain of round trips. A round trip is one message going from the phone to a server and one answer coming back. The time it takes is the RTT, the round-trip time. Most steps of the journey cannot start until the step before it has had its answer, so their RTTs add up.
Think of a cook who sends a runner to a market 15 minutes away. The runner can carry a lot, so the size of the shopping list barely matters. What matters is how many times the runner has to go back: once for the flour, then again because the recipe card in the flour bag says "you also need eggs", then again for the spice the eggs' label mentions. Three trips is 90 minutes, whatever the runner carries.
Pages work the same way: the HTML names the CSS, the CSS names a font, and the JavaScript fetches data that names an image. Each "you also need" is another trip.
Latency and bandwidth are different things
Bandwidth is how many bytes per second the connection can carry once data is flowing. Latency is how long one message takes to arrive; RTT is twice that. Phone plans advertise bandwidth, but for loading a page, latency usually matters more.
Take a page whose HTML is 30 KB after compression. On a 10 Mbps connection, sending 30 KB takes about 24 ms. Getting to the point where the server can send it (DNS, TCP, TLS, and the request) takes four round trips, which is 600 ms at 150 ms each. Doubling the bandwidth saves 12 ms; halving the RTT saves 300 ms.
| Connection | Typical RTT | Typical download speed | Four round trips cost |
|---|---|---|---|
| Office fibre, nearby server | 5 to 20 ms | 100+ Mbps | 20 to 80 ms |
| Home cable or DSL | 15 to 40 ms | 20 to 300 Mbps | 60 to 160 ms |
| Good 4G | 40 to 70 ms | 10 to 50 Mbps | 160 to 280 ms |
| Slow 4G or busy cell (Lighthouse mobile profile) | 150 ms | 1.6 Mbps | 600 ms |
| 3G or weak signal | 300 to 600 ms | 0.4 to 1 Mbps | 1.2 to 2.4 s |
Why the RTT is so high on a phone
Part of it is physics. Light in optical fibre travels about 200,000 km per second, so 1,000 km of cable adds about 5 ms each way, 10 ms per round trip. A user in Mumbai talking to a server in Virginia, about 13,000 km away, can never get an RTT below about 130 ms, and real routes are longer than straight lines. That is why CDNs put servers close to users.
The rest comes from the mobile network itself. The radio has to get a time slot from the cell tower, the signal goes through the carrier's core network before it reaches the internet, and a phone that has been idle needs to wake its radio first, which can add 100 ms or more to the first request. On a crowded or moving cell, packets get lost and are sent again, and each loss costs another round trip.
A formula you can do in your head
To estimate any phase of a page load, add four things. You will use this formula in every chapter.
- Sequential round trips times RTT. Count only the trips that must happen one after the other. Trips that happen in parallel count once.
- Bytes divided by bandwidth. Only for the big files; small files are lost in the noise.
- Server time. How long the server thinks before it starts answering: a cache hit at a CDN is 1 to 5 ms, a page built from a database can be 100 to 500 ms.
- CPU time on the device. Parsing HTML, running JavaScript, layout, paint, and image decoding. A budget Android phone does the same work 3 to 5 times slower than a recent laptop.
Check your understanding · Exercise 1
A page's first paint on slow 4G (150 ms RTT, 1.6 Mbps) is 2.6 s. Its HTML is 20 KB and its only CSS file is 15 KB, served from a separate static. domain. Which changes would cut the first paint by more than 100 ms? Select all that apply.
Shortcuts first, then DNS
What the browser checks before using the network
The fastest network request is the one the browser never makes, so it tries a few shortcuts first. They take a millisecond or two, and each one can skip most of the journey.
- URL or search? The address bar decides whether
pasta recipesis a search andrecipes.example/pastais an address. Chrome may even start connecting to a likely site while you are still typing. - HSTS and HTTPS upgrades. If the site sent a
Strict-Transport-Securityheader (HSTS, HTTP Strict Transport Security) on an earlier visit, or is on the HSTS preload list built into browsers, the browser rewriteshttp://tohttps://itself, with no request. DevTools shows this as a307 Internal Redirect. Recent versions of Chrome also tryhttpsfirst for any otherhttplink and fall back tohttponly if that fails. - Back/forward cache. On Back or Forward, the browser may restore the whole previous page from memory, frozen as it was. No network, no parsing, near-instant.
- Service worker. If the site installed one, it gets the request first and may answer from its own cache.
- HTTP cache. If a fresh copy of the HTML is cached, the browser uses it. Many sites mark HTML
no-cache, so the browser still has to ask the server whether it changed, but the files the HTML names often come straight from the cache.
If none of these answer, the browser needs to talk to a server. The URL holds a name, recipes.example, but the network routes packets to numbers. Turning one into the other is the job of DNS, the Domain Name System.
The resolver chain
DNS is a chain of caches with an authority at the end. The browser checks its own cache, then asks the operating system, which asks a recursive resolver, a server run by your internet provider, your company, or a public service such as 1.1.1.1 or 8.8.8.8. If the resolver has the answer cached, it replies at once. If not, it does the walk itself:
- It asks a root server "who knows about
.example?" The root answers with the servers for that top-level domain (TLD). Resolvers keep this answer for days, so this step is almost always cached. - It asks a TLD server "who knows about
recipes.example?" The TLD answers with the site's authoritative name servers, often run by the DNS host or CDN. - It asks the authoritative server "what is the address of
recipes.example?" and gets the real answer, such asA 203.0.113.10, along with a TTL, the number of seconds the answer may be cached. - It caches every answer for its TTL and passes the final address back to the operating system, which caches it and passes it to the browser, which caches it too.
A popular name is almost always in the resolver's cache, because many other users asked for it recently. So a typical lookup costs one round trip to the resolver: 1 to 5 ms at home on fibre, 30 to 150 ms on a phone network. A name nobody nearby has asked for recently pays for the full walk, often 100 to 300 ms more, while a name in the browser's or the OS's own cache costs nothing.
The records a frontend engineer meets
| Record | What it says | Example | Watch out for |
|---|---|---|---|
A | The IPv4 address of a name | recipes.example A 203.0.113.10 | Several A records let the client pick another address if one fails. |
AAAA | The IPv6 address of a name | recipes.example AAAA 2001:db8::10 | Browsers look up A and AAAA together and race connections to both ("Happy Eyeballs") so a broken IPv6 path does not stall the page. |
CNAME | This name is an alias for another name | www.recipes.example CNAME recipes.cdn-host.net | On a cache miss, the resolver must look up each alias in the chain in turn. Keep chains to one hop. |
HTTPS | How to connect: supported protocols such as h3, alternative endpoints, and more | recipes.example HTTPS 1 . alpn=h2,h3 | Lets a browser that reads it use HTTP/3 on the very first visit instead of learning about it from a response header. |
TTLs: the speed against agility trade
The TTL is set by whoever runs the domain, and it is a real choice. A long TTL, say 3600 (one hour) or 86400 (one day), means more lookups are answered from caches, which is faster and puts less load on the authoritative servers. But if you need to move the site to a new IP address, some users keep using the old address until their cached copy expires.
A short TTL, 30 to 300 seconds, lets a CDN or load balancer steer users quickly: away from a failing data centre, or to the nearest healthy edge. The cost is more lookups that miss the cache. CDNs commonly use short TTLs on the names they hand out for exactly this reason.
How DNS sends users to a nearby server
CDNs use DNS to put users near a server in two ways. With GeoDNS, the authoritative server looks at where the request came from and returns the address of a nearby edge, so a user in Mumbai and one in London get different answers for the same name. With anycast, the CDN announces the same IP address from hundreds of locations, and internet routing delivers each user's packets to the closest one. Many CDNs combine both.
Browsers can also send DNS queries encrypted (DNS over HTTPS, or DoH) to resolvers such as 1.1.1.1, so the network in between cannot read or change them. If that resolver is further away than the provider's own, a CDN using GeoDNS may pick a less nearby edge.
Starting DNS early from your page
You cannot speed up the lookup for your own page's name: it happens before your HTML arrives. But your HTML can tell the browser about other names it will need soon, so the lookup (and even the connection) happens while the browser is busy parsing.
<!-- Look up the name only: very cheap, fine for many origins -->
<link rel="dns-prefetch" href="https://images.recipes-cdn.example">
<!-- Look up, connect, and do TLS now: use for 1 to 3 origins you need in the first second -->
<link rel="preconnect" href="https://fonts.recipes-cdn.example" crossorigin>
crossorigin to be the one the font uses.Check your understanding · Exercise 2
Your site's A record has a TTL of 86400. At 10:00 you point it at a new server and shut the old one down at 10:05. Which statements are true? Select all that apply.
Opening a connection: TCP, TLS 1.3, and QUIC
The browser now has an IP address. Before it can send GET /pasta, it needs a connection that is reliable (nothing lost, nothing out of order) and private (nobody in between can read or change it). Over HTTP/1.1 and HTTP/2 that takes two handshakes, TCP and then TLS; HTTP/3 merges them into one.
TCP: one round trip to say hello
TCP (Transmission Control Protocol) numbers every byte, so the receiver can put them back in order and ask again for anything lost. To agree on starting numbers, the client sends SYN, the server answers SYN-ACK, and the client sends ACK with its first data right behind it. So the handshake costs one round trip.
TLS 1.3: one more round trip for privacy
TLS (Transport Layer Security) is the S in HTTPS. It proves the server really is recipes.example and sets up the keys that encrypt everything after it. TLS 1.3, which almost every browser and CDN uses today, does both in one round trip:
- ClientHello. The browser lists its ciphers, the server name it wants (SNI, so one IP address can host many sites), the protocols it speaks (ALPN, for example
h2), and its half of a key exchange, the key share. Sending the key share this early is the main change in 1.3. - ServerHello and the rest. The server picks the cipher and protocol and sends its own key share, so both sides can compute the same secret key. Then, encrypted, it sends its certificate, a signature proving it holds the certificate's private key, and
Finished. - Client Finished plus the request. The browser checks that the certificate is signed by a trusted authority, covers
recipes.example, and has not expired, then sends its ownFinishedwith the encrypted HTTP request in the same flight.
Finished. TLS 1.2 needed one more round trip here.TLS 1.2 needed two round trips, because the key exchange only started after the first hello. Saving that round trip on every new connection is the main reason HTTPS now costs so little extra.
Resumption and 0-RTT
After a full handshake, the server can hand the browser a session ticket that skips the certificate exchange next time, though resuming this way still takes one round trip. With 0-RTT (zero round-trip) resumption, the browser sends its request in the very first flight, encrypted with a key from the last session.
The catch is replay: an attacker who records that flight can send it again, and the server cannot easily tell. So servers and CDNs accept 0-RTT data only for requests that change nothing when repeated, like a GET for a page. In browsers it is most common with HTTP/3.
Slow start: why the first 14 KB are special
A new TCP connection does not know how fast the path is, so the server starts carefully. It sends a first burst of about 10 packets, roughly 14 KB, then waits for the client to confirm them. Each round trip without loss, it doubles the burst: 14 KB, then 28, then 56. This is called slow start.
So on a fresh connection, a 14 KB response arrives in one round trip, a 40 KB one needs two, and a 100 KB one three or four, whatever the bandwidth. Hence the advice to fit the critical part of the page (the head, inline critical CSS, the first screen of HTML) in the first 14 KB.
QUIC and HTTP/3, in passing
QUIC is a newer transport that runs on UDP with TLS 1.3 built in, and HTTP/3 is HTTP on QUIC. One handshake sets up transport and encryption together, so a new connection costs one round trip instead of two, and resumption can be 0-RTT. QUIC also fixes two weaknesses of TCP:
- No head-of-line blocking across requests. Over TCP, one lost packet stalls every HTTP/2 request on the connection until it is resent. QUIC gives each request its own stream, so a loss stalls only that stream.
- Connection migration. A TCP connection dies when the phone moves from Wi-Fi to mobile data and its IP address changes. QUIC identifies a connection by an ID, so it can survive the switch when both sides support migration.
A browser learns that a server speaks HTTP/3 from an Alt-Svc: h3=":443" header on an earlier response (so the first visit still uses TCP), or, if it reads them, from the HTTPS DNS record. Where a network blocks UDP, browsers fall back to HTTP/2 over TCP.
| Setup | Round trips before the request is sent | Time to first byte at 150 ms RTT (DNS cached, 50 ms server) |
|---|---|---|
| TCP + TLS 1.2 | 3 (TCP 1, TLS 2) | about 650 ms |
| TCP + TLS 1.3 | 2 (TCP 1, TLS 1) | about 500 ms |
| QUIC (HTTP/3) | 1 | about 350 ms |
| QUIC with 0-RTT resumption | 0 | about 200 ms |
| Reused open connection | 0 | about 200 ms |
Reusing connections
Handshakes are paid per connection, not per request. HTTP/2 and HTTP/3 carry many requests at once on one connection, so later requests to the same origin ride on it for free. HTTP/1.1 carries one request at a time, so browsers open up to 6 connections per host, each with its own handshakes.
So the number of origins (scheme plus host plus port) a page uses matters: every new origin can mean a new DNS lookup, TCP handshake, TLS handshake, and slow start.
Decision Which protocols should the site serve?
What we pick: Serve HTTP/2 and HTTP/3 together, which any major CDN does with a setting. HTTP/3 helps most where users suffer most: high RTT and lossy mobile networks. Keep HTTP/1.1 only as the last fallback, and stop domain sharding if you still do it.
Check your understanding · Exercise 3
A first-time visitor on slow 4G (150 ms RTT) opens https://recipes.example/ over HTTP/2 and TLS 1.3. The name is not in the phone's cache but is in the provider's resolver. The server answers in 50 ms. Which statements are true? Select all that apply.
The HTTP request, the response, and redirects
With an open, encrypted connection, the browser finally asks for the page. The request and response are plain structured text (HTTP/2 and HTTP/3 pack them into binary frames and compress the headers, but the meaning is the same). Reading them well is a daily skill: the Network panel in DevTools shows exactly these.
The request
GET /pasta HTTP/2
Host: recipes.example
User-Agent: Mozilla/5.0 (Linux; Android 10; K) ... Chrome/141.0.0.0 Mobile
Accept: text/html,application/xhtml+xml,*/*;q=0.8
Accept-Language: en-IN,en;q=0.9
Accept-Encoding: gzip, deflate, br, zstd
Cookie: session=8f2c...; consent=yes
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: cross-site
Host line travels as the :authority pseudo-header, and Chrome's reduced User-Agent always says Android 10; K instead of the real version and model.Accept-Encodinglists the compression formats the browser can undo. The server picks one, usually Brotli (br) for HTML.Cookiecarries every cookie set for this site, on every request to it. Over HTTP/1.1, 4 KB of cookies on 80 requests is 320 KB of upload on a phone's weak uplink; HTTP/2 and HTTP/3 compress repeated headers, but large cookies still cost on every new connection.Sec-Fetch-*headers tell the server what kind of request this is.navigateplusdocumentmeans a top-level page load, which servers can use to block attacks that load the page in other ways.
The response
content-type decide what the browser does at all; content-encoding decides how many bytes travel. strict-transport-security removes the http redirect from every later visit.The status line and headers arrive first, then the body. Three headers decide what the browser does next.
Content-Type: text/html tells it to render the body as a page (with Content-Disposition: attachment, it would download it instead). Content-Encoding: br tells it to decompress. Cache-Control tells it whether it may reuse this copy next time. A 304 Not Modified instead of 200 means "your cached copy is still good", and no body is sent.
What the server does inside TTFB
From the browser's point of view, the server's part of TTFB is a black box: the time between sending the request and the first byte coming back, minus one RTT. Inside the box, the cost varies wildly:
- CDN cache hit: the edge has a copy, 1 to 5 ms.
- CDN miss: the edge asks the origin, which may be on another continent. That adds the edge-to-origin round trip (often 50 to 200 ms, plus a new connection if none is open) and the origin's work.
- Server-side rendering: the origin queries a database and APIs, then renders HTML. 100 to 500 ms is common; a slow API call makes it worse.
Two techniques let the browser start working before the server has finished. Streaming sends the HTML in pieces: the server flushes the head (with its CSS and script links) at once, then the body as data arrives. 103 Early Hints goes further: before the real response, the server sends an informational 103 response with Link headers, so the browser can preconnect and preload while the page is still being built. Chrome, Edge, and Firefox act on both kinds of hint, Safari only on preconnect, and other browsers ignore it.
HTTP/2 103
Link: </css/main.3f9a.css>; rel=preload; as=style
Link: <https://img.recipes-cdn.example>; rel=preconnect
HTTP/2 200
Content-Type: text/html; charset=utf-8
...
Redirects: the most expensive response with no content
A redirect is a response with a 3xx status and a Location header that says "ask over there instead". It has no useful content, but it costs a full request, and if the new URL is on another origin, a full new DNS lookup, TCP handshake, and TLS handshake too.
| Status | Meaning | Method on the next request | Cached by the browser? |
|---|---|---|---|
301 Moved Permanently | The resource lives at the new URL from now on | May change POST to GET | Yes, by default |
302 Found | Go there this time | May change POST to GET | Only if headers say so |
307 Temporary Redirect | Go there this time, same method | Kept | Only if headers say so |
308 Permanent Redirect | Moved for good, same method | Kept | Yes, by default |
Chains of redirects are common and easy to miss, because each hop is "just one small rule" owned by a different team. A link shared on social media can go through the platform's link shortener, then to http://recipes.example/pasta, then to the https version, then to the www host, then to a language-specific path. Four hops, three of them to new origins.
- Link to the final URL everywhere you control: in emails, ads, sitemaps, and your own internal links.
- Remove the
httphop with HSTS. SendStrict-Transport-Security: max-age=31536000; includeSubDomainsso returning visitors upgrade tohttpswithout a request. To cover first visits too, addpreloadand submit the domain to the preload list at hstspreload.org. - Pick one canonical host and redirect straight to the final URL in one hop, not one rule after another.
- Avoid redirects that depend on the user, like sending
/to/en/based onAccept-Language. Serve the right content at/, or use links with the language in them from the start.
Check your understanding · Exercise 4
On slow 4G (150 ms RTT), a user follows http://recipes.example/pasta, which redirects to https://recipes.example/pasta, which redirects to https://www.recipes.example/pasta. Nothing is cached, the site is not on the HSTS preload list, and the link opens in an in-app browser that does not upgrade http links itself. Which statements are true? Select all that apply.
From the first byte to every file the page needs
The first bytes of HTML have arrived. This chapter follows them only as far as the network is concerned: how one HTML file turns into dozens of further requests, and in what order. Rendering itself belongs to the critical rendering path lesson.
Committing the navigation
In Chrome, the browser process makes the network request, not the tab. When the response headers say text/html, it picks a renderer process, the process that will run the page (on desktop, one per site, to keep sites apart). It streams the response to that process and commits the navigation: the address bar changes and the new document starts. Every browser starts parsing before the whole body has arrived.
Parsing as the bytes stream in
The HTML parser turns bytes into DOM nodes as they arrive, a chunk at a time. A script tag without async, defer, or type="module" pauses the parser until the script has downloaded and run, and a stylesheet in the head holds back the first paint until it arrives.
So that a paused parser does not stall the downloads too, browsers run a preload scanner: a light second reader that skims ahead in the raw HTML, spots src and href attributes, and starts those downloads early. It only finds URLs written plainly in the HTML.
Discovery chains
Every file the HTML names is a subresource. Some are named directly in the HTML; the browser finds those in the first pass. Others are only named inside another file, so the browser cannot even ask for them until that file has arrived and been read. Each link in such a chain adds at least one round trip, and often a new connection.
- Web fonts are named in CSS, and browsers only download a font once they know some text on the page uses it. So a font is at least two steps away from the HTML: HTML, then CSS, then font.
- CSS background images are also found only after the CSS is parsed and an element using them is styled.
- Images in client-rendered apps are the worst case: HTML, then the JavaScript bundle, then the JavaScript runs and calls an API, then the JSON names the image, then the image downloads. Four steps, each paying at least one RTT plus its own transfer time.
Priorities: who goes first
When many files are wanted at once on one HTTP/2 or HTTP/3 connection, the browser tells the server how important each one is. Chrome, for example, gives the highest priority to the HTML, the CSS in the head, and fonts; high priority to blocking scripts; and starts images at low priority, raising the ones that turn out to be in the viewport after layout. Async scripts and images below the fold stay low.
You can steer this. fetchpriority="high" on the hero image tells the browser it matters before layout proves it. rel="preload" makes a late-discovered file (a font, a background image, the LCP image of a client-rendered page) visible to the browser right away, cutting the chain short.
<head>
<link rel="preconnect" href="https://img.recipes-cdn.example">
<link rel="preload" href="/fonts/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="/css/main.3f9a.css">
<script src="/js/app.81c2.js" defer></script>
</head>
<body>
<img src="https://img.recipes-cdn.example/pasta-1200.avif" fetchpriority="high"
width="1200" height="800" alt="Bowl of tomato pasta with basil">
</body>
Think first
A client-rendered product page (an empty div in the HTML, all content from a 300 KB script) has an LCP of 4.5 s on slow 4G. The image itself is only 80 KB. Where is the time going?
Mostly into the discovery chain. The browser downloads the HTML, then the 300 KB script (about 1.5 s of transfer at 1.6 Mbps), then runs it on a slow CPU, then waits for an API round trip, then requests the image, possibly from a new origin with its own handshakes. Only then does an 80 KB download start.
The fixes shorten the chain: render the HTML on the server so the img tag is in the first response, or at least preload the image URL in the HTML and preconnect to its origin.
The timeline of a page load
You cannot fix what you cannot see. Browsers record a timestamp for every phase of the journey and expose them through the Navigation Timing API, plus a few paint timestamps through other performance APIs. Learning these names turns "the page is slow" into "DNS is fine, but the redirect costs 700 ms".
The phases, in order
All times are milliseconds since the navigation started (startTime, which is 0). Each phase is the gap between two timestamps on one PerformanceNavigationTiming entry.
| Phase | From | To | What a big number means |
|---|---|---|---|
| Redirects | redirectStart | redirectEnd | One or more 3xx hops. Reported as 0 if any hop crossed origins, so check DevTools too. |
| Service worker | workerStart | fetchStart | The service worker had to start up before it could handle the request. |
| DNS | domainLookupStart | domainLookupEnd | The name was not cached nearby. 0 means it was. |
| TCP + TLS | connectStart | connectEnd | New connection and handshakes. 0 means a connection was reused. |
| TLS alone | secureConnectionStart | connectEnd | The TLS part of the line above. |
| Waiting for the server | requestStart | responseStart | Server think time plus one RTT. |
| Downloading the HTML | responseStart | responseEnd | A large document, slow start, or a server that streams slowly. |
| Parsing until the DOM is ready | responseEnd | domInteractive | Blocking scripts or a huge document. |
Deferred scripts and DOMContentLoaded handlers | domInteractive | domContentLoadedEventEnd | Heavy defer scripts or DOMContentLoaded listeners. |
Everything else, then load | domContentLoadedEventEnd | loadEventEnd | Images, iframes, and late subresources still loading. |
PerformanceNavigationTiming entry, such as domainLookupStart to domainLookupEnd. Over half of the time to first byte here is DNS and handshakes; the load event comes long after the page looks done.Reading the numbers from the page
const [nav] = performance.getEntriesByType("navigation") as PerformanceNavigationTiming[];
const phases = {
redirect: nav.redirectEnd - nav.redirectStart,
dns: nav.domainLookupEnd - nav.domainLookupStart,
connect: nav.connectEnd - nav.connectStart,
tls: nav.secureConnectionStart > 0 ? nav.connectEnd - nav.secureConnectionStart : 0,
wait: nav.responseStart - nav.requestStart,
ttfb: nav.responseStart, // since startTime, so it includes everything before it
download: nav.responseEnd - nav.responseStart,
domInteractive: nav.domInteractive,
protocol: nav.nextHopProtocol, // "h2", "h3", or "http/1.1"
};
navigator.sendBeacon("/rum", JSON.stringify(phases));
nextHopProtocol tells you how many of them actually got HTTP/3.Three milestones: TTFB, FCP, LCP
Out of all those timestamps, three milestones matter most, because they line up with what the user feels.
- TTFB, time to first byte:
responseStart. It includes redirects, service worker startup, DNS, connection, and the server's think time, so it is the total cost of the network journey to the HTML. Good is 800 ms or less. - FCP, first contentful paint: the first time any text, image, or non-white canvas appears. The end of the blank white screen. Good is 1.8 s or less.
- LCP, largest contentful paint: when the largest image or text block in the viewport was painted, usually the hero image or the headline. The moment the page looks loaded. Good is 2.5 s or less. LCP is one of the three Core Web Vitals, and it is measured until the user first scrolls, taps, or types.
| Metric | Good | Needs improvement | Poor |
|---|---|---|---|
| TTFB | 0.8 s or less | 0.8 to 1.8 s | over 1.8 s |
| FCP | 1.8 s or less | 1.8 to 3.0 s | over 3.0 s |
| LCP | 2.5 s or less | 2.5 to 4.0 s | over 4.0 s |
Read them as a chain: FCP can never come before TTFB, and LCP can never come before FCP.
If TTFB is 1.9 s, the page cannot have a good LCP on a phone no matter how lean the frontend is. If TTFB is 400 ms but FCP is 3 s, the time is going into render-blocking files, and the critical rendering path lesson is where to look. If FCP is fast but LCP is 2 s later, the largest element is being discovered or downloaded late.
The recipe page, step by stepStep 1 of 8: 0 ms: tap
recipes.example, so it needs the network. The screen stays white.0 ms: tap: Priya taps the link. The browser finds nothing cached for recipes.example, so it needs the network. The screen stays white.
Lab numbers and field numbers
Lab tools (Lighthouse, WebPageTest, the DevTools Performance panel) load the page once under fixed conditions: good for debugging, because every run is comparable. Field data comes from real users, through your own code like the snippet above, the web-vitals library, or Chrome's public CrUX dataset. Only field data tells you what your users get, because only it includes their phones, their networks, and their caches.
Check your understanding · Exercise 5
Field data at the 75th percentile shows: redirect 0 ms, DNS 0 ms, connect 0 ms, requestStart to responseStart 1,400 ms, FCP 2.1 s, LCP 2.3 s. Which conclusions follow? Select all that apply.
Where the time goes on a slow phone
Let's go back to Priya on the train and account for every millisecond of her 4.2-second wait for the recipe photo. The conditions are the ones Lighthouse uses for its mobile score: 150 ms RTT, 1.6 Mbps download (about 200 KB per second), and a phone whose CPU is about 4 times slower than a developer laptop.
The page
- The shared link is
http://recipes.example/pasta, which redirects tohttps. It opens in the messaging app's built-in browser, which does not upgradehttplinks itself. - The HTML is 40 KB compressed, rendered on the server for each request in about 200 ms.
- The stylesheet (30 KB) is on
static.recipes.example, a second origin. - The heading font (40 KB) comes from a third-party font service, a third origin, and text waits for it.
- The hero photo (150 KB) is on an image CDN, a fourth origin, as a plain
imgin the HTML. - A 350 KB JavaScript bundle loads with
defer.
The accounting
| Step | Cost | Clock |
|---|---|---|
DNS for recipes.example | 1 RTT | 150 ms |
TCP to port 80, request, 301 back (plain http, no TLS) | 2 RTT + 20 ms | 470 ms |
| New TCP and TLS 1.3 to port 443 | 2 RTT | 770 ms |
| Request, 200 ms of server rendering, first byte | 1 RTT + 200 ms | 1,120 ms (TTFB) |
| CSS: DNS, TCP, TLS, request, 30 KB on the second origin | 4 RTT + 160 ms | 1,880 ms |
| Font: found in the CSS, then DNS, TCP, TLS, request, 40 KB on the third origin | 4 RTT + 250 ms | 2,730 ms |
| Style, layout, and paint on a slow CPU | about 100 ms | 2,830 ms (FCP) |
| Photo: connected early, but its 150 KB share the 1.6 Mbps link with 460 KB of other files | bandwidth | about 4,170 ms |
| Decode and paint the photo | about 50 ms | about 4,220 ms (LCP) |
Three lessons fall out of the table. First, more than a quarter of the wait happens before the first byte of HTML, and almost none of it is the server's fault: it is the redirect and the handshakes. Second, the first paint is set by a chain of three origins (page, stylesheet, font), each paying four round trips. Third, the LCP is set by bandwidth: 610 KB at 200 KB per second is three seconds of transfer, and the photo was competing with a bundle more than twice its size.
The CPU side
The model leaves out the JavaScript's own cost, but a phone feels it. 350 KB of compressed JavaScript is around 1.2 MB of code once decompressed, and a budget Android phone can spend one to two seconds parsing, compiling, and running it on the same main thread that handles taps.
The page looks ready at 4.2 s but ignores the first tap until well after. For a client-rendered page, that CPU time also sits in front of the LCP, because the content only exists after the code runs.
Fixing it, one phase at a time
| Fix | Phase it removes | Saving on slow 4G |
|---|---|---|
Link to the final https URL and add HSTS | The redirect hop | about 320 ms off everything |
| Cache the HTML at the CDN edge (or stream it) | Server rendering time inside TTFB | about 180 ms |
| Serve HTTP/3 | One handshake round trip per new connection | 150 ms per origin |
| Serve the CSS from the page's own origin | DNS, TCP, and TLS for the stylesheet | about 450 ms |
| Self-host the font and preload it from the HTML | A whole origin, and the wait for the CSS | nearly 0.9 s off FCP |
fetchpriority="high" on the photo | Waiting behind the JavaScript for bandwidth | about 1.3 s off LCP |
With all six, the same page on the same phone gets a TTFB of about 470 ms, an FCP of about 930 ms, and an LCP of about 1.8 s. Not one byte of the page was removed. Try the fixes one at a time below, and switch networks to see why none of this shows up on office Wi-Fi.
What breaks in production
- Third-party tags creep in. Each tag manager, A/B test, chat widget, and analytics script brings its own origin. Sort the DevTools Network panel by its Domain column and count: more than about 10 origins in the first few seconds is a warning sign.
- The long tail misses the cache. The home page is always cached at the CDN; the 50,000 recipe pages are not. Field TTFB for rarely visited pages can be several times the home page's.
- DNS is a single point of failure. When the DNS provider Dyn was hit by a large attack in October 2016, many well-known sites were unreachable for hours though their servers were fine. Large sites now use more than one DNS provider.
- Shared caches no longer help. Browsers now partition the HTTP cache by the top-level site, so a font from a popular public CDN is downloaded again for every site that uses it. The old argument that "users already have it cached" is gone, which is one reason teams self-host fonts and libraries.
Check your understanding · Exercise 6
A news site's field data at the 75th percentile: TTFB 2.1 s, FCP 2.6 s, LCP 3.0 s. Most visits come from social media links. Which are good first places to look? Select all that apply.
"What happens when you type a URL?" in an interview
This question is popular because it has no bottom. A junior candidate can give a correct answer in a minute; a senior candidate can talk for an hour and still be learning. The interviewer is checking two things: do you know the whole journey in the right order, and can you go deep on any part of it with real reasons and numbers.
The one-minute answer
Start with the whole path, briefly, so the interviewer sees you know the shape. Then offer to go deep. A good opening sounds like this:
- "The browser checks whether the input is a URL or a search, upgrades to HTTPS if HSTS says so, and tries the back/forward cache, the service worker, and the HTTP cache."
- "If it needs the network, it resolves the name with DNS: its own cache, the OS, then a recursive resolver."
- "It opens a TCP connection and does a TLS 1.3 handshake, one round trip each, or a single QUIC handshake for HTTP/3."
- "It sends the GET request; the server or CDN answers. A redirect sends it back to the DNS or connection step."
- "It streams the HTML into the parser, and the preload scanner fetches the CSS, scripts, fonts, and images it names. Render-blocking CSS and scripts gate the first paint."
- "It builds the DOM and CSSOM (the style tree), then runs style, layout, paint, and compositing. FCP is the first paint, LCP the largest element."
- "Where would you like me to go deeper: the network, the rendering, or performance?"
Then draw. A horizontal timeline with the phases as blocks (DNS, TCP, TLS, request, server, download, parse, paint) and the RTT written above the first few is the most useful drawing, because every follow-up question becomes "which block gets bigger or smaller?"
How deep to go, by level
| Level | What a strong answer adds |
|---|---|
| Junior | All the steps in the right order, with what each one is for. Knows DNS turns names into addresses, TLS encrypts, and CSS and blocking scripts delay the first paint. |
| Mid-level | Costs: round trips per step, TLS 1.3 against 1.2, slow start and the 14 KB idea, the effect of extra origins and redirects. Can read a waterfall in DevTools and name TTFB, FCP, and LCP with their thresholds. |
| Senior | The trade-offs and the measurement: DNS TTLs and failover, HTTP/3 and 0-RTT with replay risk, CDN cache misses inside TTFB, Early Hints and streaming, discovery chains and priorities, field against lab data, and a plan for what to fix first on a slow phone. |
Common mistakes
- Going down the network stack instead of along the journey. ARP, Ethernet frames, and routing tables are rarely what a frontend interviewer wants. Mention that packets are routed and move on unless asked.
- Forgetting the shortcuts. Caches at every layer (bfcache, service worker, HTTP cache, DNS caches, open connections) are what make the journey fast in practice.
- Listing without costs. "Then TLS happens" is weaker than "then TLS 1.3, one more round trip, about 150 ms on a slow phone".
- Stopping at the first paint. Subresources, LCP, and the JavaScript's CPU time are where most real pages lose their time.
Bringing it into a system design round
In a frontend system design interview, nobody asks you to recite the journey, but it shapes many decisions.
When the requirements mention global users, mobile users, or a fast first load, say how you would budget the time before the first byte: a CDN close to users, HTML cached or streamed at the edge, HTTP/2 and HTTP/3, few origins on the critical path, and no redirects on shared links. Then say how you would check it: Navigation Timing and LCP from real users, at the 75th percentile, split by country and connection type.
Check your understanding · Exercise 7
The interviewer asks: "Our checkout page has a 2.4 s TTFB for users in Southeast Asia. Our servers are in Frankfurt. What would you do?" Which points belong in a strong answer? Select all that apply.
Summary
- A page load is a chain of sequential round trips. On a slow phone each one costs about 150 ms, so counting them predicts load time better than counting kilobytes.
- A cold HTTPS visit pays DNS, TCP, TLS, and the request, about four round trips before the first byte. TLS 1.3 needs one round trip, TLS 1.2 two, and QUIC folds transport and TLS into one.
- DNS is a chain of caches ending at an authority. TTLs trade speed and low load against how quickly changes and failovers reach users.
- Every new origin and every redirect can repeat the whole connection cost. Keep the critical path on one origin and link to final URLs.
- TCP slow start means the first round trip carries only about 14 KB, which is why the critical part of the HTML should come first and be small.
- Discovery chains (HTML to CSS to font, or HTML to JavaScript to API to image) add round trips; preload, preconnect, server rendering, and
fetchpriorityshorten them. - Navigation Timing splits TTFB into redirect, DNS, connect, and server wait. Read TTFB, FCP, and LCP as a chain, and judge them on field data at the 75th percentile.
In the interview
Questions interviewers ask about this. Answer each out loud first, then compare.
Question 1
Walk me through what happens when you type https://shop.example into the address bar and press Enter.
The browser decides it is a URL, applies HSTS if it knows the site, and checks the back/forward cache, a service worker, and the HTTP cache. Then it resolves shop.example through its own DNS cache, the OS cache, and a recursive resolver, which may walk from the root to the TLD to the authoritative server. Every answer is cached for its TTL.
It opens a TCP connection (one round trip) and runs a TLS 1.3 handshake (one more), negotiating HTTP/2 with ALPN, or uses a single QUIC handshake for HTTP/3. It sends the GET request with cookies; the CDN or server answers. A 3xx sends it back to DNS or the connection step for the new URL.
The response headers say text/html, so the browser commits the navigation to a renderer process and streams the body into the HTML parser. The preload scanner fetches the CSS, scripts, fonts, and images, reusing the connection where it can and opening new ones for other origins. Render-blocking CSS and scripts gate the first paint; then style, layout, paint, and compositing put pixels on screen. FCP is the first content, LCP the largest element. I can go deeper on any step.
Question 2
Why is a first visit on a phone so much slower than on a laptop, even when the page is small?
Because the cost of a cold page load is mostly sequential round trips, and the phone's RTT is much higher: 150 ms on slow 4G against 10 to 20 ms on office fibre. DNS, TCP, TLS, and the request are four round trips before the first byte, and every extra origin or redirect adds three or four more.
On top of that, TCP slow start limits the first round trips to about 14 KB, 28 KB, and so on, and a budget phone's CPU runs parsing and JavaScript several times slower. Small pages do not escape the round trips.
Question 3
What does TTFB include, and how would you bring it down?
TTFB, measured as responseStart, includes redirects, service worker startup, DNS, the TCP and TLS handshakes, the round trip of the request, and the server's think time. Good is 800 ms or less at the 75th percentile.
Split it with Navigation Timing to see which part is big. Then: link to final URLs and use HSTS to remove redirects; put a CDN close to users so handshakes are short; cache or stream the HTML at the edge; enable HTTP/3 to save a handshake round trip; and use Server-Timing to find slow backend calls. While the server works, 103 Early Hints can let the browser start on CSS and connections.
Question 4
What are the trade-offs of short and long DNS TTLs?
A long TTL means more lookups are answered by caches, so users pay less DNS time and the authoritative servers see less load. But changes take as long as the TTL to reach everyone, which hurts during a migration or an outage.
A short TTL, 30 to 300 seconds, lets a CDN or load balancer steer users quickly to healthy, nearby servers, at the cost of more cache misses. Before a planned move, lower the TTL at least one old TTL in advance.
Question 5
What does HTTP/3 change for page loads, and is 0-RTT safe?
HTTP/3 runs on QUIC over UDP, which merges the transport and TLS 1.3 handshakes into one round trip, keeps a lost packet from stalling unrelated requests, and lets connections survive a switch from Wi-Fi to mobile data. Browsers find it through the Alt-Svc header or the HTTPS DNS record and fall back to HTTP/2 when UDP is blocked.
0-RTT resumption sends the request with the first packet, using a key from the previous session. That first flight can be replayed by an attacker, so servers accept 0-RTT only for requests that are safe to repeat, like a GET for a page, never for a payment or other change.
References
- Populating the page: how browsers work MDN's overview of the journey from navigation to paint.
- Time to First Byte (TTFB) What TTFB includes, its thresholds, and how to improve it.
- Largest Contentful Paint (LCP) How LCP is defined and measured.
- PerformanceNavigationTiming Every timestamp of the navigation, with its meaning.
- RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3 The handshake, resumption, and 0-RTT, including the replay warning.
- RFC 9114: HTTP/3 HTTP over QUIC, and how clients discover it.
- High Performance Browser Networking Ilya Grigorik's free book on latency, TCP, TLS, and mobile networks.
Read next
- The critical rendering pathEverything the browser must fetch and compute before the first pixels: HTML, render-blocking CSS, parser-blocking scripts, then style, layout, paint, and composite, and how to count, measure, and shorten it.
- DNS, CDNs, and the edgeHow a name becomes an address, how that address leads to a server a few milliseconds from the user instead of an ocean away, and what code and files a frontend should run and keep there.
- HTTP/1.1, HTTP/2, and HTTP/3The same requests carried three ways (one at a time per TCP connection, many at once on one TCP connection, or many at once on QUIC over UDP), which decides bundling, sprites, sharding, and how a lossy network feels.
- Inside the browserHow a modern browser splits its work across processes and threads, why your JavaScript shares one main thread with layout, paint, and input, and how the event loop, workers, and OffscreenCanvas decide whether a page stays responsive.
- Core Web VitalsThree numbers, measured on real users, that say whether a page loads fast (LCP), reacts fast (INP), and stays still (CLS): what each one measures, what makes it bad, and how to fix it.
- HTTP cachingThe browser's built-in cache, steered by headers on each response, which lets a repeat visit skip the network entirely or ask only "has this changed?": the cheapest speed-up on the web.