Accurate request timing is central to understanding how responsive an HTTP, HTTPS, or API endpoint feels under real conditions. Measuring latency from the client side exposes slow paths caused by DNS lookups, TCP handshakes, TLS negotiation, or server processing, and enables focused troubleshooting instead of blind tuning. Consistent measurements also establish baselines for later performance regressions.

The cURL command-line tool exposes a set of timing variables that describe each phase of a request lifecycle. Using the --write-out option, cURL can print values such as {time_namelookup}, {time_connect}, {time_appconnect}, {time_starttransfer}, and {time_total} after each request. Combined with --silent and --output /dev/null, these metrics appear without progress meters or response bodies, producing clean, script-friendly timing lines.

Timing values always represent the perspective of the host running cURL, so local DNS caches, proxies, VPNs, or transient congestion can heavily influence readings. Multiple samples are usually required to separate normal jitter from real problems, and high-concurrency or soak tests are better delegated to dedicated load-testing tools. Excessive timing runs against production services risk triggering rate limits or paging alerts, so conservative sampling protects both endpoints and monitoring noise budgets.

Steps to measure request timing with cURL:

  1. Run a single HTTPS request that prints only the total time taken in seconds.
    $ curl "https://www.example.com" --write-out "Time_Total: %{time_total}\n" --silent --output /dev/null
    Time_Total: 0.247891

    The time_total variable reports the complete duration of the request, including DNS lookup, TCP connection, TLS handshake, and data transfer.

  2. Expose key timing phases in a single run by including multiple variables in the format string.
    $ curl "https://www.example.com" \
      --write-out "DNS=%{time_namelookup} Connect=%{time_connect} TLS=%{time_appconnect} TTFB=%{time_starttransfer} Total=%{time_total}\n" \
      --silent --output /dev/null
    DNS=0.004103 Connect=0.012944 TLS=0.040512 TTFB=0.120871 Total=0.231604

    time_namelookup covers DNS resolution, time_connect tracks the TCP handshake, time_appconnect measures TLS setup, and time_starttransfer approximates Time To First Byte (TTFB).

  3. Compare latency across endpoints or environments by reusing the same timing format with different URLs.
    $ curl "https://api.internal.example.com/health" \
      --write-out "Target=internal Total=%{time_total}\n" \
      --silent --output /dev/null
    Target=internal Total=0.043512
    
    $ curl "https://api-eu.example.com/health" \
      --write-out "Target=eu Total=%{time_total}\n" \
      --silent --output /dev/null
    Target=eu Total=0.127903

    Tight timing loops against production APIs can generate significant traffic, trigger rate limiting, or create noisy alert spikes on monitoring dashboards.

  4. Gather a short series of measurements in a shell loop to observe typical latency and outliers.
    $ for i in $(seq 1 5); do \
    >   curl "https://www.example.com" --write-out "%{time_total}\n" --silent --output /dev/null; \
    > done
    0.228910
    0.241773
    0.236501
    0.239884
    0.230742

    A tight cluster of values suggests stable network and server behavior, while sporadic spikes highlight contention, garbage collection pauses, or network variability.

  5. Verify that timing output behaves as expected by testing an intentionally slow endpoint and confirming higher values for time_starttransfer and time_total.
    $ curl "https://slow.example.com" \
      --write-out "TTFB=%{time_starttransfer} Total=%{time_total}\n" \
      --silent --output /dev/null
    TTFB=0.842110 Total=1.037922

    Success signals: formatted timing lines appear after each request, values are numeric with decimal seconds, and deliberately slower endpoints consistently report larger totals than fast health-check URLs.

Discuss the article:

Comment anonymously. Login not required.