Skip to content
StatusCheck

Reading an HTTP timing waterfall: DNS, TCP, TLS, TTFB, download

What each phase of an HTTP check measures, what "normal" looks like, and how to tell a slow origin from a slow network, a slow resolver, or an expensive TLS handshake.

StatusCheck team 4 min read

“The API is slow” is where most investigations start and where too many of them stop. An HTTP request has five distinct phases, each with its own causes and its own fixes. A monitor that records all five, which is what a timing waterfall is, turns “slow” into a specific question.

Here is a real check from a StatusCheck probe in Frankfurt to an endpoint behind a CDN:

Phase Time
DNS 0.7 ms
TCP connect 9.4 ms
TLS handshake 30.1 ms
Time to first byte 18.3 ms
Download 0.6 ms
Total 59.1 ms

Let’s walk through what each number means and what it looks like when it goes wrong.

DNS, 0.7 ms

The time to turn api.example.com into an IP address. Sub-millisecond here means the probe’s resolver had the answer cached. An uncached lookup to a well-run authoritative server is typically 10–50 ms; over 200 ms usually means a slow or distant authoritative server, a long CNAME chain, or a resolver having a bad day.

When it spikes: it’s almost never your application. Check the TTLs on your records (very short TTLs mean constant uncached lookups), the number of CNAME hops, and whether your DNS provider is reachable from the region that’s slow. A DNS monitor against your authoritative server, from the same regions, tells you whether the source of truth is slow or the resolver in between is.

When it’s zero: the target was an IP address, so there was nothing to resolve.

TCP connect, 9.4 ms

The time for the three-way handshake to complete: one round trip to the server’s edge. This number is a clean measure of network distance. Frankfurt to a Frankfurt CDN edge: under 10 ms. Frankfurt to a US-East origin with no CDN: 90–110 ms. Sydney to the same origin: 200+ ms.

When it spikes: something on the path changed: a routing shift, a saturated link, or your edge under load and slow to accept connections. If TCP is slow from one region and normal from the rest, it’s the path. If it’s slow from everywhere, it’s your edge.

When it fails: connection refused means the host answered and said no (nothing listening on that port). A timeout means nothing answered at all, usually a firewall, a dead host, or a route that goes nowhere.

TLS handshake, 30.1 ms

Negotiating the encrypted session. With TLS 1.3 this is one round trip plus the cryptography; expect roughly 1–3× your TCP time. 30 ms on a 9 ms connection is normal. TLS 1.2 without session resumption is two round trips, so noticeably more.

When it’s large: a slow or overloaded TLS terminator, a long certificate chain (each intermediate is bytes on the wire), an RSA certificate where ECDSA would be faster, or OCSP stapling that isn’t set up so clients go and check revocation themselves. It’s also the phase that grows first when your load balancer is CPU-bound.

When it fails: the certificate is expired, doesn’t match the hostname, or the chain is incomplete. StatusCheck records the certificate’s expiry on every HTTPS check and an SSL monitor will tell you which of those it is.

When it’s zero: the target was plain http://.

Time to first byte, 18.3 ms

From the request leaving the probe to the first byte of the response arriving. This is the phase your application owns. It includes one network round trip plus everything your server did: routing the request, hitting the database, rendering, and any upstream calls.

Subtract the TCP time (one round trip) and you have the server’s own processing time, here about 9 ms, which is what a cached response from a CDN edge looks like. A dynamic endpoint doing real work is typically 50–300 ms. Over a second is the phase to investigate first, and it is where nearly every “the API is slow” story ends up.

When it spikes: a slow query, a cold cache, a full-GC pause, a saturated worker pool, or a slow upstream your endpoint waits on. If it spikes from every region at once, it’s the origin. If it spikes from one region, check whether that region hits a different backend.

Download, 0.6 ms

Reading the rest of the body after the first byte. Small for small responses; scales with body size and bandwidth. A health endpoint should be near zero. If download is a large share of total time, the response is big. Check whether compression is on and whether the endpoint is returning more than the check needs.

Putting it together

A few patterns show up often enough to name:

  • Everything is fine except TTFB. Your application. Start with the slowest query.
  • TCP and TLS are both high, TTFB is normal. Network distance or an overloaded edge. Put a CDN or a regional endpoint closer to users, or check your load balancer’s CPU.
  • DNS is high, everything else is normal. Your resolver chain, not your app. Check TTLs and CNAME depth.
  • One region is slow across all phases. The path from that region. Interesting if that’s where your customers are; otherwise noise.
  • Total is fine but redirects > 0. Every redirect is a full extra round of DNS + TCP + TLS + TTFB. Point the monitor at the final URL, or fix the redirect.

The waterfall doesn’t fix anything on its own. What it does is turn a vague complaint into a phase, a phase into an owner, and an owner into a fix, usually before anyone opens a profiler.

Every StatusCheck HTTP check records all five phases from every region it runs in. The HTTP monitor docs list the full set of fields.

httpperformancemonitoring

Monitoring that stays quiet until it matters.

Early access opens in batches. No spam, one email when it's your turn.

Free plan, no card needed