← Recipes

阅读中文版本

Break a Slow HTTP Response into DNS, TLS, and Server Time

Use the curl write-out timer to split one slow request into name lookup, connect, TLS, time-to-first-byte, and transfer phases, then aim the fix at the right layer.

~15 minapiperformancedebugging

Task

Split a slow HTTP request into per-phase timings with curl and identify which phase dominates the latency.

When to use this

A page takes 900 ms to load its first API call. Everyone has a theory: DNS, the CDN, the database. Nobody has measured anything.

Prerequisites

  • curl 7.75+ for the full write-out variables
  • The slow URL reproducible from your machine
  • Permission to run repeated requests against the target

Steps

1. Run one timed request with the full breakdown format.

curl -s -o /dev/null -w "dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n" https://api.example.com/v1/orders

Expected output: Each phase prints in seconds, for example dns=0.004 connect=0.021 tls=0.089 ttfb=0.742 total=0.760, exposing server think time as the dominant cost.

2. Repeat the request to separate cold from warm costs.

for i in 1 2 3 4 5; do curl -s -o /dev/null -w "ttfb=%{time_starttransfer} total=%{time_total}\n" https://api.example.com/v1/orders; done

Expected output: If the first request shows tls=0.250 and later ones show tls=0.001, session reuse is working and TLS cost is a cold-start issue only.

3. Compare against a known-fast endpoint on the same host.

curl -s -o /dev/null -w "ttfb=%{time_starttransfer}\n" https://api.example.com/healthz

Expected output: A 12 ms health check on the same hostname proves network and TLS are fine and the orders endpoint itself is slow.

4. Point the evidence at the right owner with a server-side trace.

curl -s -H "X-Trace-Id: slow-001" -o /dev/null https://api.example.com/v1/orders && grep "slow-001" /var/log/app/traces.log | head -3

Expected output: The server log shows the request id and the internal span that consumed most of the 730 ms, handing the problem to the exact owner.

Troubleshooting

Problem

dns stays at tens of milliseconds

Solution

Switch the test machine resolver to a closer one and check whether the record has a low TTL; deploy DNS caching in the client or a local stub resolver.

Problem

ttfb dominates even on the health endpoint

Solution

The server or an intermediary proxy queues requests; measure from a different network to rule out your own uplink before blaming the origin.

Problem

Timings vary wildly between runs

Solution

Run each measurement at least five times and take the median; single samples mix in routing changes and garbage-collection pauses.

Limitations

  • The timer attributes phases, not causes; a large ttfb still needs server-side tracing to name the guilty span.
  • Through proxies or VPNs the connect phase may include hidden hops that the variables cannot separate.

One request, five clocks

curl can timestamp every phase boundary of a request: name lookup done, TCP connected, TLS done, first byte received, and transfer complete. Printing them side by side answers the question that arguments cannot: is this slow because of the network, the handshake, or the server holding the connection open while it thinks? The format string is the whole tool, and it works against any URL without server changes.

curl -s -o /dev/null -w \
  "dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n" \
  https://api.example.com/v1/orders

Read the gaps, not the totals

The variables are cumulative timestamps, so each phase cost is the difference to the previous one: TLS cost is time_appconnect minus time_connect, and pure server think time is time_starttransfer minus time_appconnect. A big gap between tls and ttfb with a tiny total afterward means the body was fast to transfer and the wait was entirely server-side.

Always compare against a control

A single URL gives you numbers but not a verdict. Hit a known-cheap endpoint on the same hostname, such as a health check: if its ttfb is a few milliseconds, network and TLS are exonerated in one step, and the expensive part of the slow endpoint is code you own.