Skip to content

TTFB and asset delivery

Time to First Byte (TTFB) is the most sensitive performance metric for CDN-delivered content. It captures the full latency of the request-response cycle up to the moment the first byte of the response body arrives at the client: DNS lookup, TCP handshake, TLS negotiation (first request per connection), and the server’s processing time before it begins sending. For a warm CDN cache serving a small asset, TTFB should be in the tens of milliseconds. For an origin-fetching response, it includes the full round trip to your data centre.

What TTFB measures (and what it doesn’t)

Section titled “What TTFB measures (and what it doesn’t)”

TTFB = time_to_first_byte - request_start

This includes:

  • DNS resolution — negligible after the first request per runner, but significant for the first connection.
  • TCP/TLS handshake — negligible after connection establishment, especially with HTTP/2 connection reuse.
  • CDN processing time — the edge node looks up the cached response or fetches from origin.
  • Origin RTT (cache miss only) — the time for the CDN edge to fetch from your origin and begin responding.

TTFB does not include:

  • Download time — the time to receive the full response body after the first byte. For large assets this dominates total response time.
  • DNS-to-CDN routing quality — TTFB starts after DNS resolution. If DNS points to a distant PoP, TTFB reflects that routing, not a CDN cache problem.
Cache stateAsset sizeExpected TTFB
Hit, same-continent edge< 100 KB5–30 ms
Hit, cross-continent edge< 100 KB30–100 ms
Miss (origin fetch)Any sizeOrigin RTT + processing time (50–500 ms typical)
Stale-while-revalidateAny sizeFast (served stale) while origin revalidate happens async

If your p50 TTFB for a cached small asset is above 100 ms, the cache is likely cold, traffic is routing to a distant PoP, or the CDN is bypassing cache on that request.

MaxoPerf records http_req_duration (total response time from request start to last byte) and the underlying timing components for each request. In the run overview, the latency percentile chart shows p50, p90, p95, and p99. For CDN assets, p95 TTFB is the headline metric to gate on.

To focus on TTFB specifically, keep assets small in your test and note that total response time ≈ TTFB for small assets (< 10 KB). For large assets, separate TTFB from download time by labelling them differently.

execution:
- concurrency: 200
ramp-up: 1m
hold-for: 5m
scenario: ttfb-measurement
scenarios:
ttfb-measurement:
default-address: https://cdn.example.com
requests:
# Small asset — total time ≈ TTFB
- label: small-asset-ttfb
url: /static/favicon.ico
method: GET
assert:
- equals:
subject: http-code
value: '200'
# Large asset — measures total transfer time, not just TTFB
- label: large-js-bundle
url: /static/vendor.bundle.js
method: GET
assert:
- equals:
subject: http-code
value: '200'
# API response (usually small, tests CDN API caching)
- label: cached-api-response
url: /v1/catalog/featured
method: GET
assert:
- equals:
subject: http-code
value: '200'

Large static assets (video files, ZIP archives, firmware images) add download-time complexity on top of TTFB. CDNs typically serve large assets using chunked transfer encoding or support HTTP range requests, allowing partial downloads.

scenarios:
large-asset-range:
requests:
# Full download
- label: full-video-manifest
url: /video/episode-01.m3u8
method: GET
# Range request — test CDN range-request caching
- label: video-segment-range
url: /video/episode-01-segment-001.ts
method: GET
headers:
Range: bytes=0-1048575 # first 1 MB
assert:
- equals:
subject: http-code
value: '206' # partial content

A CDN that does not properly cache range requests will return 200 with the full file on each request (cache miss for each range). Test for 206 Partial Content responses and verify that subsequent range requests for the same byte ranges return quickly (cache hit).

CDNs typically serve compressed responses (gzip or Brotli) when the request includes Accept-Encoding. Compressed responses transfer faster, reducing total response time for large text-based assets (JS, CSS, HTML, JSON).

To test compression:

scenarios:
compression-test:
requests:
- label: compressed-js
url: /static/app.js
method: GET
headers:
Accept-Encoding: gzip, br
assert:
- contains:
subject: headers
value: 'Content-Encoding: gzip' # or br for Brotli

If the CDN is not returning compressed responses despite a valid Accept-Encoding header, check:

  • CDN compression configuration (many CDNs require compression to be explicitly enabled).
  • Whether the origin sends pre-compressed assets or relies on the CDN to compress on the fly (on-the-fly compression adds CPU cost and may not be cached as a separate variant without proper Vary handling).

Modern CDNs use HTTP/2 or HTTP/3 (QUIC) for connections between load runners and the edge. This has implications for your test results:

  • Connection reuse — HTTP/2 multiplexes multiple requests over a single TCP connection. The first request pays TLS handshake overhead; subsequent requests reuse the connection. This means p50 TTFB in a sustained run is lower than the first-request overhead suggests.
  • Concurrent streams — a single HTTP/2 connection can carry many in-flight requests simultaneously, reducing the per-VU connection overhead.
  • HTTP/3 / QUIC — uses UDP; eliminates TCP head-of-line blocking. If your CDN supports HTTP/3, test with it explicitly to measure the latency benefit vs HTTP/2.

MaxoPerf runners support HTTP/2 by default when the server advertises it via ALPN. Check the http_version label in your run metrics to confirm.

Use MaxoPerf failure criteria to gate releases on TTFB targets:

  • p95 TTFB < 50 ms for small, regularly-cached static assets served from a nearby region.
  • p95 TTFB < 150 ms for API responses cached at the CDN layer.
  • Error rate < 0.1 % for all CDN-served requests.

See Failure criteria for how to configure these gates in the MaxoPerf console.

Do:

  • Use small assets (< 10 KB) to measure TTFB in isolation from download time.
  • Warm the cache before measuring steady-state TTFB — cold-cache TTFB is dominated by origin RTT.
  • Test both compressed and uncompressed responses if you serve a mix.

Don’t:

  • Treat total response time as TTFB for large assets — the two are only equal for very small responses.
  • Ignore HTTP version differences between MaxoPerf environments and production — HTTP/1.1 vs HTTP/2 can produce TTFB differences of 20–50 ms.
  • Set TTFB failure criteria without accounting for multi-region variance — a 50 ms threshold appropriate for a co-located test may be too tight for a cross-continental region.