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.
TTFB benchmarks for CDN assets
Section titled “TTFB benchmarks for CDN assets”| Cache state | Asset size | Expected TTFB |
|---|---|---|
| Hit, same-continent edge | < 100 KB | 5–30 ms |
| Hit, cross-continent edge | < 100 KB | 30–100 ms |
| Miss (origin fetch) | Any size | Origin RTT + processing time (50–500 ms typical) |
| Stale-while-revalidate | Any size | Fast (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.
Measuring TTFB in MaxoPerf
Section titled “Measuring TTFB in MaxoPerf”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.
Taurus test for TTFB measurement
Section titled “Taurus test for TTFB measurement”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 asset and range-request testing
Section titled “Large asset and range-request testing”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.
Range request pattern
Section titled “Range request pattern”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 contentA 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).
Compression and content encoding
Section titled “Compression and content encoding”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 BrotliIf 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
Varyhandling).
HTTP/2 and HTTP/3 considerations
Section titled “HTTP/2 and HTTP/3 considerations”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.
Setting TTFB failure criteria
Section titled “Setting TTFB failure criteria”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 / don’t
Section titled “Do / don’t”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.
Where to go next
Section titled “Where to go next”- Multi-region edge performance — measure TTFB from multiple geographic locations.
- Cache hit/miss testing — ensure the cache is warm before TTFB measurements.
- Origin shield and offload testing — understand how origin fetch RTT affects TTFB on cache misses.
- Failure criteria — gate your CDN tests on TTFB thresholds.