Orange Book
CDN and web performance

08 · Edge TTL, Browser TTL, and cache response headers

Separate browser, Cloudflare edge, and origin cache lifetimes, then use Cache-Control and CDN-Cache-Control to build an updateable release policy.

Edited and verified by Orange Book Editorial Team ·

CDN · PHASE 3INTERMEDIATEAbout 28 minutesEdge TTL · Browser TTL · Revalidation

Outcome

Browsers will not hold mutable URLs for an unsafe period, Cloudflare can retain public objects longer than browsers, fingerprinted resources can use long lifetimes safely, and private responses explicitly forbid shared storage. Releases will have a clear choice between a new URL, revalidation, and targeted purge.

Cache TTL layers between a browser, the Cloudflare edge, and an origin
Browser TTL and Edge TTL belong to different storage layers. Purging Cloudflare cannot remotely recall a fresh object already stored by a browser.Open original
Detailed description
A request travels from a browser to the Cloudflare edge and reaches the origin when necessary. The browser uses Cache-Control max-age. The Cloudflare edge can use Cloudflare-CDN-Cache-Control, CDN-Cache-Control, s-maxage, or a Cache Rule Edge TTL. The origin owns content generation and version validators.
  1. 01
    Browser

    Uses downstream Cache-Control and Browser TTL to decide whether to send a network request.

  2. 02
    Cloudflare edge

    Uses Edge TTL and revalidation policy to choose HIT, UPDATING, or an origin request.

  3. 03
    Origin

    Generates content and returns cache semantics, ETag, or Last-Modified.

  4. 04
    Release system

    Changes URLs for immutable assets and targets mutable content with short TTLs or purges.

Do not mix the three lifetime layers

LayerPrimary controlSuitable policyHardest mistake to roll back
BrowserCache-Control: max-age, Browser TTLConservative for mutable URLs; long for fingerprinted URLsOne year on unversioned HTML or JS
Cloudflare edgeEdge TTL, shared-cache headers, Cache RulesPublic content can outlive the browser copyForcing private or frequently changing responses
Origin/applicationResponse headers, ETag, Last-Modified, version URLsKeep content semantics as the source of truthFailing to distinguish public, private, and immutable

When a browser still considers an object fresh, it sends no request to Cloudflare. A later edge purge cannot prevent that user from seeing the local copy. Avoid hard-to-revoke Browser TTLs on unversioned pages and scripts.

Divide responsibilities across headers

Header or directivePrimary consumerPurpose
Cache-Control: max-age=...Browsers and caches not separated by a dedicated headerClient freshness
s-maxage=...Shared cachesOverrides max-age in shared cache; browsers ignore it
Cloudflare-CDN-Cache-ControlCloudflareDedicated Cloudflare edge policy
CDN-Cache-ControlCDNs that support the fieldGeneric CDN policy separate from browsers
Edge TTL Cache RuleCloudflareOverrides edge-lifetime headers for matching requests
Browser TTL Cache RuleDownstream responseOverrides Cache-Control behavior sent to browsers

Do not combine s-maxage and stale-while-revalidate casually

In Cloudflare's current behavior, s-maxage implies proxy-revalidate and prevents a shared cache from serving stale content. If you need a separate edge lifetime while retaining asynchronous stale-while-revalidate, Cloudflare recommends origin max-age plus stale-while-revalidate, with Cache Rule Edge TTL setting Cloudflare freshness separately.

Three reviewable templates

Fingerprinted static asset

Cache-Control: public, max-age=31536000, immutable

Use only for JS, CSS, fonts, and images whose URL contains a content hash and changes whenever the bytes change. Do not copy it to mutable URLs such as /app.js or /latest.pdf.

Updateable public HTML or API

Cache-Control: public, max-age=60
Cloudflare-CDN-Cache-Control: public, max-age=300, stale-while-revalidate=30
ETag: "content-version-42"

The browser asks again after one minute while Cloudflare can retain the object for five minutes and refresh asynchronously within the allowed window. These numbers are conservative examples, not universal defaults; tune them to update frequency, stale-content cost, and origin capacity.

Private page or job result

Cache-Control: private, no-store

Use for accounts, billing, administration, private image/PDF results, and permissioned data. A very short TTL does not make private shared caching safe.

Choose an Edge TTL mode in Cache Rules

ModeBehaviorSuitable use
Respect headers; bypass when absentA response without cache headers is not storedGoverned applications where safety comes first
Respect headers; use Cloudflare defaults when absentKeeps default static-extension behaviorConventional static-site migration
Ignore headers and use rule TTLForces the selected lifetimeNarrow, proven-public objects only

The last mode overrides the origin's freshness and potentially privacy intent. If you use it, exclude Set-Cookie, identity routes, and private APIs and validate with two accounts.

Verify TTL and revalidation

curl -sS -D first.headers -o /dev/null https://www.example.com/assets/app.abc123.js
curl -sS -D second.headers -o /dev/null https://www.example.com/assets/app.abc123.js

Inspect Cache-Control, Cloudflare-CDN-Cache-Control, Age, CF-Cache-Status, ETag, and Last-Modified. Age grows on hits and resets after revalidation, purge, or eviction. With stale-while-revalidate, a request after expiry can show UPDATING; that is the expected asynchronous-refresh state.

Next, apply these layers to CMS, SaaS, and AI-built applications.

Primary sources

Did this page help you complete your goal?

Beta feedback is generated in this browser and is never uploaded automatically.

On this page