All posts

Why is a CDN still slow or stale? Cache keys, TTL, ETag, and release design

· 8 min read

Diagnose old content and slow requests across browser, CDN, and origin caches. Five tips connect cache keys, max-age versus s-maxage, 304 revalidation, and releasing content-hashed assets on a public site.

Why can a site remain slow or stale after adding a CDN?

First establish whether the slow request used a cached response. A CDN stores copies at edge locations. A hit can reuse a copy; a miss continues to an upstream cache or the origin. A hit in one region does not establish the cache state in another.

Simplified delivery path: each cache has its own copy and freshness

Browser copy → CDN edge copy → Upstream cache, if configured → Current origin content

Separate hits, misses, and revalidation

Inspect the URL, response headers, and timings in developer tools. Correlate the provider’s cache-status header with origin logs and measure regional TTFB, or time to first byte. Measurements with browser caching disabled do not reproduce a returning visitor using an old fresh copy.

ObservationPossible pathNext check
Only misses are slowWaiting for upstream fetch or origin workOrigin time, location, key fragmentation
Frequent revalidationChecking an expired copy upstreamFreshness, validators, validation cost
Only some visitors see old codeOld browser copy or entry HTMLActual HTML references and asset filenames

Cache keys: share only equivalent responses

A 320px image and a 640px image of product 42 differ. If changing only utm_source leaves the body identical, that tracking value can be excluded. Forwarding a value to the origin and including it in the cache key may be separate settings. Preserve distinctions for headers that change language or format too.

Illustrative public-image keys, not provider configuration syntax
/image?id=42&width=320&utm_source=mail -> key=(42,320)
/image?id=42&width=320&utm_source=ad   -> key=(42,320)
/image?id=42&width=640&utm_source=mail -> key=(42,640)

Do not declare personalized responses safe merely by adding a user ID to the key and marking them public. This case avoids shared storage for member pages and checks authorization and cache policy separately. Verify that image sizes and languages cannot mix before tuning hit rate.

Distinguish max-age, s-maxage, and ETag

Illustrative public HTML response, assuming the CDN stores it and honors these directives
Cache-Control: public, max-age=0, s-maxage=300
ETag: "directory-v7"

RFC 9111 makes s-maxage override freshness for shared caches. Here the browser checks before reuse, while the CDN may answer from a fresh copy. With Age 240 seconds, about 60 seconds remain; browser validation does not necessarily reach the origin. Check minimum TTLs, overriding rules, and cache eligibility in the actual configuration.

An expired copy can be checked with If-None-Match and reused after a 304 if unchanged. For 2MB JSON this avoids refetching the body from the origin, but retains the round trip and validation work. CloudFront documents constraints on conditional requests when forwarding cookies; the presence of an ETag alone does not establish the expected behavior.

Still stale after purge: fix asset names and release order

Overwriting /app.js does not invalidate fresh browser copies when the CDN is purged. Publish a content-hashed URL such as /app.c3d4.js, then update entry HTML to reference it. Stale HTML still points to old assets, so design entry-page revalidation and acceptable shared-cache staleness too.

  1. Publish new JS, CSS, and images and verify availability; treat their URLs as immutable.
  2. Switch entry HTML references and, if needed, verify completion of CDN invalidation for HTML.
  3. Retain old files while old pages or open tabs may reference them.
  4. Verify HTML references and running versions for new and returning visitors across regions.

Verify performance, correctness, and update propagation

Compare regional hit and miss timings, origin requests, body bytes, and errors. Products classify revalidated requests differently, so align metric definitions. Increasing freshness to 300 seconds is not a promise of current content; validate accepted staleness and actual propagation against user requirements.

Primary sources

The official sources below were checked on October 27, 2026. Scenarios and numerical examples are illustrative assumptions. Configuration examples are explanatory; they have not been run against live services or benchmarked.

Tips this post digs into