Measuring with --write-out

curl · Practical · 10 min

Quick latency breakdowns and status checks from the shell are invaluable when debugging slow or flaky services.

-w / --write-out prints a format string after the transfer, with %{variable} placeholders filled in. Combine it with -o /dev/null -s to print only what you ask for.

Common variables:

  • %{http_code} the response status, %{url_effective} the final URL after redirects, %{num_redirects}.
  • %{size_download}, %{speed_download}, %{content_type}, %{http_version}, %{remote_ip}.
  • Timings in seconds, each measured from the start of the request: %{time_namelookup} (DNS), %{time_connect} (TCP), %{time_appconnect} (TLS done), %{time_starttransfer} (first byte, often called TTFB), %{time_total}.

Because timings are cumulative, subtract neighbours to get each phase: TLS time is time_appconnect - time_connect; server think time is roughly time_starttransfer - time_appconnect.

Other handy forms: -w '%{json}' prints every variable as one JSON object (pipe it into jq), %{header_json} gives the response headers as JSON, %header{content-type} a single header, -w @format.txt reads the format from a file, and %{stderr} switches the rest of the output to stderr. \n in the format becomes a newline.

A status check one-liner: code=$(curl -s -o /dev/null -w '%{http_code}' "$URL"), then branch on $code.

Example

curl -s -o /dev/null -w '%{http_code}\n' https://example.com          # 200
curl -sL -o /dev/null -w '%{url_effective} after %{num_redirects}\n' http://github.com
curl -s -o /dev/null -w 'dns=%{time_namelookup} tcp=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n' https://example.com
# dns=0.012 tcp=0.031 tls=0.068 ttfb=0.142 total=0.143
curl -s -o /dev/null -w '%{json}' https://example.com | jq '{http_code, time_total, http_version}'
code=$(curl -s -o /dev/null -w '%{http_code}' https://api.example.com/health)
[ "$code" = 200 ] || echo "unhealthy: $code"

Remember

  • -s -o /dev/null -w '...' prints only the metrics you want
  • Timings are cumulative from the start; subtract to get phases
  • -w '%{json}' dumps every variable as JSON

Watch out for

  • Reading %{time_starttransfer} as server time alone; it includes DNS, TCP and TLS.

Check yourself

  1. `time_connect` is 0.030 and `time_appconnect` is 0.095. What does 0.065 s represent?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: curl.se/docs/manpage.html#-w