Keystroke to painted pixel
Where do the milliseconds go?
Press Enter and four sequential round trips run before a single byte of the page comes back. Here is the budget, at a 50ms round trip to a CDN edge.
~200ms TTFB
Cold connection: DNS, TCP, TLS, and HTTP each cost one round trip, summing to a ~200ms time to first byte.
Every competitor tells you what happens. This page tells you where the milliseconds go, and where each layer fails.
What happens when you type a URL and press enter, timed to the millisecond
A latency-budgeted, failure-mapped trace of one real web request on the 2026 stack. Every phase gets a millisecond budget, the command that reproduces its number, and the exact signature it shows when it breaks.
Where do the milliseconds actually go?#
Most explainers list the stages in order and stop there. However a list of names does not help you fix a slow page. The useful question is where the time goes, so start with the felt gap. A cold connection and a warm one differ by a factor of two, and the difference is pure network round trips.
~200ms
Cold first visit (HTTP/2 + TLS 1.3)
~100ms
Warm repeat visit (HTTP/3 + 0-RTT)
A cold connection pays four sequential round trips to first byte. A warm HTTP/3 visit folds two of them away, halving TTFB before the server does any work.
Show data table
| Option | network time to first byte, 50ms round trip to the edge |
|---|---|
| Cold first visit (HTTP/2 + TLS 1.3) | ~200ms |
| Warm repeat visit (HTTP/3 + 0-RTT) | ~100ms |
That halving is the whole story in miniature. Moreover it repeats at every layer. Each protocol upgrade removes measurable round trips, and each removed round trip is one 50ms slice you get back. So the rest of this page decomposes both numbers and shows you how to measure your own.
What happens when you type a URL and press enter: the whole journey#
Before the details, here is the whole path on one diagram. First the browser does a surprising amount of work with no packet on the wire. Then four network round trips fetch the first byte. Finally the render path turns bytes into pixels. Read the map once, and every section below has a home on it.
Before a single packet leaves: the browser's pre-flight#
Most top results skip everything the browser does before the network. Yet a large part of what happens when you type a URL and press enter finishes before any byte hits the wire. The browser parses your text, decides whether it is even a URL, upgrades the scheme, and checks its caches. Consequently the fastest possible request is the one that never leaves the machine.
URL parsing: how the browser splits scheme, host, and path#
First the browser parses your text against the WHATWG URL Standard. It splits the string into a scheme, a host, an optional port, a path, a query, and a fragment. Each part routes differently later. For example the fragment after the hash stays in the browser and is never sent to the server.
https://www.example.com:443/blog/what-happens?ref=hn#top
scheme https
host www.example.com
port 443 (implicit for https, so it is rarely typed)
path /blog/what-happens
query ref=hn
fragment top (stays in the browser, never sent to the server) The rules here are precise, not folklore. Because the parser is a living standard, every browser splits the same string the same way. You can read the exact algorithm in the WHATWG URL Standard.
Search or navigate? The omnibox disambiguation#
The address bar is also a search box, so the browser first decides your intent. Text with a valid host and scheme is treated as a navigation. Whereas a bare phrase with spaces goes to your default search engine instead. A single word is ambiguous, so the browser may probe DNS or fall back to search. In short, not every enter press even becomes a URL request.
HSTS and the forced HTTPS upgrade#
Next the browser checks whether the site demands HTTPS. Sites on the HSTS preload list, or ones you visited before that sent the header, are upgraded from http to https in the browser itself. This upgrade happens before DNS, so no insecure request is ever sent. It is a pre-flight step almost no competitor names.
Service worker and cache: the request that never leaves#
Finally the browser checks who can answer without the network. A registered service worker can intercept the request and reply from its own cache. Meanwhile the HTTP disk and memory caches may hold a fresh copy already. A cache hit returns a 200 straight from disk, and a revalidation may return a 304 Not Modified. Both paths cost zero network round trips, which is the fastest outcome there is.
The four network round trips (and why each one costs 50ms)#
On a cache miss the request finally hits the network. This is the quantified core of what happens when you type a URL and press enter. Four phases run in strict sequence, and each one costs a whole round trip to the edge. At a 50ms round trip, that is 50ms apiece, and 200ms in total. Each phase is governed by a public specification, and each has a distinct failure signature.
DNS resolution: name to address (~1 RTT / 50ms)#
First the browser turns the host name into an IP address. A cache miss walks the recursive resolver out to the authoritative servers, which costs one round trip in the common case. The governing spec is RFC 1035, with modern terminology in RFC 8499. When it fails, the signature is clear. NXDOMAIN means the name does not exist, and SERVFAIL means the resolver could not answer.
TCP handshake: SYN / SYN-ACK / ACK (~1 RTT / 50ms)#
Next the browser opens a TCP connection with the three-way handshake. The client sends SYN, the server replies SYN-ACK, and the client sends ACK. Although that is three messages, it completes in one round trip before data flows. The current TCP specification is RFC 9293. When it fails, you see a connection refused (RST) or a connection timeout.
TLS 1.3 negotiation: one round trip, down from two (~1 RTT / 50ms)#
Then the connection is secured with TLS. Here the version matters directly to your budget. TLS 1.3 completes its handshake in one round trip. Whereas TLS 1.2 needs two, so it adds a full 50ms slice. The specification is RFC 8446. Cloudflare confirms the same one-versus-two round trip difference in its first-party writeup of TLS 1.3.
HTTP request/response: GET to first byte (~1 RTT / 50ms)#
Finally the browser sends the HTTP GET and waits for the first response byte. That request and reply is one more round trip, governed by RFC 9110. Add the four phases and the network time to first byte is 50 plus 50 plus 50 plus 50, or ~200ms. As a result you land comfortably inside Google's "good" TTFB threshold of 800ms, which is documented at web.dev.
Reproduce every number yourself#
None of this has to be taken on faith. Every number above comes from a command you can run right now. Each tab below isolates one phase of the trace. Run them against your own host and the abstract stages become real timings on your terminal.
# Walk the DNS delegation from the root, one referral at a time.
dig +trace www.example.com
# The final A record is the resolved address, with its TTL beside it.
# A short TTL means your next visit may pay for DNS all over again. # Print the cumulative time at each phase, in seconds.
curl -s -o /dev/null \
-w 'dns: %{time_namelookup}\ntcp: %{time_connect}\ntls: %{time_appconnect}\nttfb: %{time_starttransfer}\ntotal: %{time_total}\n' \
https://www.example.com/
# Subtract each phase from the one before it to get its own cost.
# time_appconnect minus time_connect is the TLS handshake alone. # Inspect the handshake and the negotiated TLS version and cipher.
openssl s_client -connect www.example.com:443 \
-servername www.example.com < /dev/null
# Look for "Protocol : TLSv1.3" and the certificate chain.
# An expired or name-mismatched certificate fails right here, before any HTTP. # Force HTTP/3 over QUIC and confirm the transport folds into the handshake.
curl --http3 -sI https://www.example.com/
# The status line reports HTTP/3 when your curl and the server both support it.
# A warm 0-RTT resumption then skips the extra transport round trips entirely. The curl -w template is the one to keep close. Because it prints cumulative times, subtracting adjacent phases gives you each phase's own cost. For instance time_appconnect minus time_connect is the TLS handshake in isolation.
Cold vs warm: what changes on the second visit#
The 2026 stack is where the biggest wins hide, and they are protocol wins. HTTP/2 kept the serial TCP-then-TLS handshake. HTTP/3 rebuilt transport on QUIC and folds the transport and crypto handshakes into one. The table below compares the three across the numbers that decide first-byte latency.
| HTTP/1.1 | HTTP/2 | HTTP/3 (QUIC) | |
|---|---|---|---|
| Transport | HTTP/1.1TCP | HTTP/2TCP | HTTP/3 (QUIC)QUIC over UDP |
| Round trips to first byte, cold | HTTP/1.14 | HTTP/24 | HTTP/3 (QUIC)3 |
| Round trips to first byte, warm | HTTP/1.12 | HTTP/22 | HTTP/3 (QUIC)2 |
| Handshake | HTTP/1.1TCP then TLS, separate | HTTP/2TCP then TLS, separate | HTTP/3 (QUIC)transport plus crypto, folded |
| Multiplexing | HTTP/1.1None, one request per connection | HTTP/2Yes, over one TCP connection | HTTP/3 (QUIC)Yes, over independent streams |
| Head-of-line blocking | HTTP/1.1At the application layer | HTTP/2Removed in HTTP, remains in TCP | HTTP/3 (QUIC)Removed at both layers |
| 0-RTT resumption | HTTP/1.1No | HTTP/2With TLS 1.3 | HTTP/3 (QUIC)Native |
Two rows carry most of the weight. First, QUIC saves a round trip cold by folding transport into the handshake. Second, 0-RTT saves two more warm by riding the first flight. The QUIC transport is specified in RFC 9000, and HTTP/3 itself in RFC 9114.
Try it: trace any request's latency budget#
Numbers in prose are easy to skim past, so here is the model as an instrument. Set the round trip to the edge, flip between a cold and a warm connection, switch the TLS version, and choose HTTP/2 or HTTP/3. Watch each phase's bar recompute and the running time to first byte update live. This is the same math the sections above describe, made touchable.
~200ms TTFB
Good, under 800ms- DNS resolution50msRecursive lookup, one round trip on a cache miss (RFC 1035).
- TCP handshake50msThree-way handshake, one round trip (RFC 9293).
- TLS 1.3 handshake50msTLS 1.3 negotiates in one round trip (RFC 8446).
- HTTP request to first byte50msGET to the first response byte, one round trip (RFC 9110).
cold first visit over HTTP/2 over TCP: 4 round trips at 50ms, a ~200ms TTFB, inside Google’s good TTFB threshold of 800ms.
After TTFB the browser still runs the critical rendering path, and render-blocking CSS or JavaScript, not the network, is often the larger share of a slow first paint.
Play with two moves in particular. Switch cold to warm and two bars fold to nothing. Then switch HTTP/2 to HTTP/3 and the transport bar folds into the handshake. Each move is a round trip you stopped paying for.
After TTFB: the critical rendering path#
The network is only half of what happens when you type a URL and press enter. Once the first bytes arrive, the browser has to turn them into pixels. This is the critical rendering path, and it is the depth most explainers skip. Crucially, render-blocking resources here can dwarf the network time you just budgeted.
Parse HTML into the DOM#
First the parser reads the HTML and builds the DOM tree, node by node. It streams as bytes arrive, so it can start before the full document lands. However a plain script tag blocks the parser while it fetches and runs. Consequently a script in the head can stall the whole page.
CSSOM and render-blocking CSS#
Meanwhile the browser builds the CSSOM from your stylesheets. CSS is render-blocking by default, because the browser will not paint until it knows the final styles. Therefore a large or slow stylesheet delays the first paint directly. The mechanics are documented in the web.dev guide to the critical rendering path.
async vs defer JavaScript#
Scripts are the render-blocking trap most posts never draw. A plain <script> blocks parsing. Whereas async runs the script as soon as it downloads, out of order. And defer waits until parsing finishes, then runs scripts in order. For anything not needed for first paint, defer keeps the parser moving.
Layout (reflow) vs paint vs composite#
Next the browser combines the DOM and CSSOM into the render tree. Then it runs layout, also called reflow, to compute the exact box for every element. After that it paints pixels, and finally it composites layers onto the screen. Because layout recomputes geometry, a change that moves boxes is more expensive than one that only repaints color. The MDN critical rendering path guide details each step.
Render sub-step to Core Web Vitals#
Each render sub-step moves a specific metric. So the map below wires them together. TTFB tracks the first byte, FCP the first paint, LCP the largest paint, and CLS the layout stability. Once you can name which step moves which metric, a bad score points straight at the phase to fix.
This is also where the platform-level work connects. If your response is rendered at the edge, our walkthrough of how Cloudflare's edge renders the page once the response arrives picks up exactly where TTFB ends. For the layer below the browser, here is another look at what's really happening under the hood in your computer. And once the page is live, here is what happens after the page loads, when the browser keeps a live connection open.
Where each layer fails (and the one command that proves it)#
A latency budget tells you where the time goes. A failure map tells you where it breaks. Each layer shows a distinct signature and a user-visible symptom, and one command isolates it. When a page is down, this table is the fastest way to find the guilty layer.
| Layer | Failure signature | User-visible symptom | Command that isolates it |
|---|---|---|---|
| DNS | Failure signatureNXDOMAIN or SERVFAIL | User-visible symptomThis site cannot be reached | Command that isolates itdig www.example.com |
| TCP | Failure signatureRST or connection timeout | User-visible symptomThe connection timed out | Command that isolates itcurl -v https://host/ |
| TLS | Failure signatureCert expired, name mismatch, or chain error | User-visible symptomYour connection is not private | Command that isolates itopenssl s_client -connect host:443 |
| HTTP | Failure signatureA 4xx or 5xx status | User-visible symptomA server error page | Command that isolates itcurl -sI https://host/ |
| Render path | Failure signatureCSS or JS blocks the first paint | User-visible symptomA blank page, then a late paint | Command that isolates itDevTools Performance panel |
Notice the pattern. Each row moves you down one layer, and each command probes exactly that layer and no other. As a result you never guess. You bisect, from name resolution down to the render path.
When this mental model misleads you#
This trace is a diagnostic tool, not a universal answer. So it is worth naming where it points you wrong. The four-round-trip budget describes transport latency, and transport is frequently not your bottleneck.
Key takeaways#
A slow or flaky request is rarely one problem. It is a budget spread across DNS, transport, the server, and the render path, and finding the guilty layer is most of the work. If you want a second set of hands on request latency, edge delivery, or a Core Web Vitals audit, we are glad to help. No pressure and no lock-in.
See how Atyantik ships production request pipelines