Seven stages, each a possible failure point
Splitting a request apart is what makes debugging possible. From typing a URL to a painted page it roughly goes: URL parsing → DNS → connecting (TCP + TLS) → sending → server processing → receiving → rendering and sub-resources. Almost every "the site is slow" complaint lands in one of these.
Stage by stage
- URL parsing: scheme, host, port, path and query are separated; a missing port falls back to the scheme default (80 for HTTP, 443 for HTTPS);
- DNS: browser cache → OS cache → hosts → recursive resolver → authoritative server, ending with an A or AAAA record;
- Connecting: the TCP three-way handshake, plus a TLS handshake for HTTPS (certificate validation, session key agreement);
- Sending: request line, headers and body — watch Content-Length, chunked transfer and cookie size;
- Server processing: routing, auth, business logic and database access — usually where most of the time goes;
- Receiving: the status code decides what happens next — redirect, cache read or error;
- Rendering and sub-resources: after parsing HTML the browser fetches CSS, JS, images and APIs, each a fresh request.
Where the time goes
| Stage | Typical cost | Common fixes |
|---|---|---|
| DNS | milliseconds to hundreds of ms | Prefetch, fewer hostnames, sane TTLs |
| TCP / TLS | 1–3 RTTs | Keep-alive, session resumption, TLS 1.3 |
| Server processing (TTFB) | Depends on the backend | Caching, SQL tuning, edge compute |
| Transfer | Proportional to size | Compression, slimmer assets, streaming |
| Render and sub-resources | Depends on front-end structure | Fewer critical assets, lazy loading |
Debugging order
- Check the status code first: 4xx is a request problem, 5xx a server problem, 3xx means counting hops;
- Then TTFB: high means a slow server or a roundabout network path;
- Open the Timing panel and confirm which stage stalls;
- Recheck from the command line to rule out the browser.
One command that is enough
curl -sS -o /dev/null -w \
'dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} \
ttfb=%{time_starttransfer} total=%{time_total} code=%{http_code}\n' \
https://example.com/
Read the numbers in order: high dns means resolution, high connect means the path, high tls means the handshake, high ttfb means the server, and total minus ttfb points at transfer or payload size.
Practical debugging: three common symptoms
- "First request slow, later ones fine": look at DNS and connection setup (cold start) first, then whether caching hit; it is usually resolution or handshake cost, not the server.
- "High TTFB but fast transfer": the problem is server-side processing (database, downstream calls) — inspect slow queries and dependencies rather than adding bandwidth.
- "High total, low in every stage": usually too many sub-resources or a stacked redirect chain; count requests and hops before optimising.
Common questions
Why is the first visit slow and later ones fast? DNS caching, connection reuse and asset caching — a slow cold start is normal. Is HTTPS always slower than HTTP? It adds 1–2 RTTs of handshake, but TLS 1.3 and resumption shrink that to little, and the security is worth it. What if there are many redirects? Each 3xx is a full round trip, so cut the chain down — especially stacked HTTP → HTTPS → www hops.
Where the time usually goes in each stage
- DNS: typically 20–120ms on a cold lookup, nearly free once cached;
- TCP + TLS: a new connection costs one RTT for the handshake plus one or two for TLS — the biggest chunk of first-paint latency, so connection reuse pays off;
- Server: shows up as TTFB, covering queueing, auth, database queries and downstream calls;
- Transfer: dominated by bandwidth and congestion window; large bodies stretch badly on slow links;
- Render: after the HTML arrives come parsing, style calculation and script execution — surface critical assets early.
Where to instrument
- Browser: Navigation Timing breaks down DNS, connection, TTFB and asset loading;
- Gateway: record upstream time and queueing to separate "slow service" from "long queue";
- Application: time database and downstream calls separately instead of lumping them into "endpoint latency";
- Join all three with one request ID to locate the slow segment.
What multiplexing changes
With HTTP/2 a browser multiplexes requests over one connection, so setup cost is amortised and domain sharding is unnecessary — but do not pile every critical asset onto one connection and risk head-of-line blocking. HTTP/3 confines loss to a single stream, which usually steadies first paint on poor networks.