TTL (Time To Live)
TTL (time to live) is how many seconds a cached response stays fresh before a cache must revalidate or refetch it. Origins set it with Cache-Control max-age, or s-maxage for shared caches such as a CDN edge. It is the main lever on cache hit ratio and origin load.
Also known as Freshness lifetime.
Full Explanation
TTL (“time to live”) is how many seconds a cache may keep a stored response and reuse it without contacting the origin again. Origins set it with the Cache-Control header, normally its max-age directive. s-maxage carries a separate value. Only shared caches such as a CDN edge apply it, so the edge and the browser can run different clocks (RFC 9111 §5.2.2.10). Choosing TTLs is the main lever you have on cache hit ratio and origin load.
A TTL is a permission to reuse. It is not a guarantee of anything else. It does not reserve storage: a cache may evict an object long before the timer runs out. Shortening a TTL does not remove copies already stored. A TTL does not reach copies already sitting in browsers, either. TTL is also unrelated to the two other timers that share the abbreviation: the hop limit in an IP datagram header (RFC 791) and the DNS record TTL (RFC 1035 §4.1.3).
How it works
HTTP carries no field called “TTL”. It carries a freshness lifetime. The TTL you configure is what ends up in it. A response is fresh while its age has not yet exceeded that lifetime. Once age exceeds the lifetime, the response is stale. Age is the time that has passed since the response was generated by, or successfully validated with, the origin server (RFC 9111 §4.2).
A cache computes the lifetime from the first rule that matches. If the cache is shared and s-maxage is present, it uses that value. Otherwise it uses max-age. Otherwise it uses Expires minus Date. Otherwise the response has no explicit expiration time at all (§4.2.1). Both max-age and s-maxage are delta-seconds: plain non-negative integers, never dates (§5.2.2.1, §1.2.2).
While a copy is fresh, the cache serves it. The origin never hears about the request (§4.2). When it turns stale, the copy is not thrown away. The cache issues a conditional request built from the validators it stored: an ETag replayed in If-None-Match, or a Last-Modified timestamp replayed in If-Modified-Since (§4.3, §4.3.1). A 304 (Not Modified) means the stored response can be updated and reused (§4.3.3). The cache refreshes the stored header fields from the 304 (§4.3.4). Age counts from the last successful validation, so the clock starts again. A full response replaces the stored copy instead. A TTL therefore governs how often a cache checks, not how often it downloads. Akamai describes exactly this loop: an If-Modified-Since GET whose negative answer resets the object’s TTL (Akamai, What is TTL?).
Serving the old bytes past expiry is the exception, not the rule. A cache MUST NOT generate a stale response unless it is disconnected, or unless doing so is explicitly permitted (§4.2.4). The usual permission is the stale-while-revalidate extension. It lets a cache return the stale response immediately and revalidate in the background, for up to the number of seconds you specify, not a fixed grace period (RFC 5861 §3). stale-if-error grants the same licence when the origin fails.
Sending no freshness information does not mean “do not cache”. With no explicit expiration present, a cache MAY assign a heuristic lifetime of its own. This only applies to status codes defined as heuristically cacheable, or to responses marked explicitly cacheable. It never applies when an explicit expiration time is present. A common heuristic takes some fraction of the interval since Last-Modified. RFC 9111 notes only that “A typical setting of this fraction might be 10%” (§4.2.2). If the number matters, send the number.
Why it matters for a CDN
A CDN is a shared cache parked next to the viewer. Every second of TTL is a second in which requests terminate at the edge server instead of crossing the network to your origin. A fresh response “can therefore reduce both latency and network overhead each time the cache reuses it” (RFC 9111 §1). That makes the TTL the primary control on cache hit ratio and origin offload. “The longer the TTL associated with an object, the greater the benefit of offloading content” (Akamai, TTL best practices). Short TTLs invert the trade: fresher content, but more revalidations. Every genuine miss pays a full cache fill from the origin.
Two properties of real CDNs change how the number behaves. First, a CDN’s cache is not one cache. On Akamai, each individual edge server has its own cache. Content is stored there only as a result of an end-user request. A long TTL therefore bounds reuse per server, rather than seeding a copy everywhere (Akamai). Second, the edge timer and the browser timer are separate decisions. s-maxage applies only to shared caches, not browser caches, and clients ignore it. That is why a long s-maxage paired with a short max-age keeps the origin offloaded, while limiting how long a stale page can survive in a browser cache you cannot reach (Google Cloud, Akamai).
What CDNs do
- Cloudflare. Origin Cache Control is enabled by default for Free, Pro and Business customers, and they cannot disable it. Only Enterprise customers can choose, through cache rules. With it enabled, a response whose max-age is 3,600 seconds is cached for that duration before Cloudflare checks the origin again. s-maxage and max-age set the edge value and the browser value respectively. Two configuration overrides sit above your header. Edge Cache TTL rules override s-maxage and disable revalidation directives. Where a Browser Cache TTL is set, Cloudflare respects whichever is higher: that setting or max-age (Cloudflare, Origin Cache Control).
- Akamai. The property configuration is the primary place a TTL is set, matched per directory, file extension or other grouping. The untouched default is to cache “for a theoretically infinite time” with least-recently-used eviction. Edge servers do not honour the origin’s Cache-Control or Expires headers until you enable Honor origin Cache-Control and Expires. After that, they act on s-maxage, max-age, no-store and no-cache (Akamai). Edge servers also prefresh. By default, once 90% of the TTL has elapsed, a request is still served from cache and additionally triggers an asynchronous If-Modified-Since check. Revalidation typically happens before expiry rather than at it (Akamai). Downstream, the client max-age is by default the smaller of the origin’s value and the object’s remaining edge lifetime. It is therefore always equal to or lower than the edge max-age (Akamai). The proprietary Edge-Control header takes precedence over Cache-Control, Expires and many caching-related configuration settings. Akamai now calls it “generally deprecated”, so do not reach for it (Akamai).
- Google Cloud CDN. max-age and s-maxage set the expiration. s-maxage applies only to shared caches. max-age applies to all caches, unless s-maxage overrides it. You can also set or override TTLs on the backend, and define a separate client-facing TTL. The default cache mode, CACHE_ALL_STATIC, caches common static content types when the origin specifies no caching directives at all. For content that changes infrequently, Google recommends a long expiration combined with versioned URLs. Google treats invalidation as a last resort (Google Cloud CDN, content delivery best practices).
Watch out for
- A TTL is not a lease on storage. Akamai’s edge servers evict on a least-recently-used basis. They can remove an object that has not yet reached its TTL. Cloud CDN evicts infrequently accessed content, and removes anything not accessed for 30 days unconditionally. A year-long max-age still buys a cold fetch on an unpopular object (Akamai, Google Cloud).
- The same header means different things per provider and per setting. On Cloudflare with Origin Cache Control on, max-age=0 means cache and always revalidate. With Origin Cache Control off, an Enterprise-only state, the same directive bypasses the cache (Cloudflare). On Akamai, an origin max-age does nothing at all until honouring it is switched on (Akamai).
- s-maxage switches off stale-serving at the edge. s-maxage incorporates proxy-revalidate semantics. A shared cache therefore MUST NOT reuse a stale response carrying s-maxage until the origin has validated it (RFC 9111 §5.2.2.10). This silently defeats stale-while-revalidate. Cloudflare says it outright: “Do not use s-maxage with stale-while-revalidate” (Cloudflare).
- A TTL of zero is not the same as no caching. A zero TTL still stores the object. It merely forces an If-Modified-Since revalidation on every request, which can be the right call for a large, time-sensitive file. If the body genuinely differs per request, no-store is the correct control instead (Akamai).
- Age is an estimate, not a countdown. The Age field conveys the sender’s estimate of the time since the response was generated or successfully validated at the origin (RFC 9111 §5.1). So “remaining TTL = max-age minus Age” is a sound debugging approximation, not an authoritative figure.
- Prefer max-age to Expires. When both are present, max-age wins (§4.2.1). A delta-seconds value needs no clock comparison at all. The Expires route is deliberately computed as Expires minus Date, so the origin’s own clock is used. When the origin omits Date, the cache substitutes the time it received the message, and skew re-enters (§4.2.1). Akamai implements the same defence, using the origin’s Date rather than the edge server’s clock (Akamai).
- immutable is browser-facing. RFC 8246’s immutable extension tells clients they SHOULD NOT issue a conditional request during the freshness lifetime, even on a reload (RFC 8246 §2). However, Cloudflare notes the directive “has no effect on public caches like Cloudflare” and only changes browser behaviour. It removes user-driven revalidation, and changes nothing about how the edge revalidates with your origin (Cloudflare).
Best practice
- Send an explicit freshness lifetime on everything meant to be cached, so nothing falls back to a heuristic you do not control (RFC 9111 §4.2.2).
- Derive the value from time sensitivity: “In general, you should choose the longest possible TTL that does not cause the end-user to receive stale content” (Akamai). Akamai’s own bands are a zero TTL or no-store for the extremely time-sensitive, an hour or more where a site is republished daily, and a very long TTL for archival content (Akamai). Google offers five seconds for near real-time sports scores, and one hour for weather updates (Google Cloud). Treat those as illustrations. Ask the operative question instead: how long am I willing to serve the old version after I have changed it?
- Version immutable assets rather than shortening their TTL. Put a build number or content hash in the URL (cache busting), and give it a long max-age plus immutable. Publishing a change then means publishing a new URL. This is the pattern RFC 8246 was written for, and the approach Google recommends by default for updating cacheable content (RFC 8246 §1, Google Cloud).
- Split the timers when the edge and the browser should not agree: a long s-maxage for the CDN, with a short max-age for browsers. If you also want background revalidation at the edge, send max-age alone. Set the edge value in the CDN’s own configuration instead, because s-maxage disables stale-while-revalidate there.
- Keep a purge path for what a TTL cannot cover, such as a wrong price or a legal takedown, and treat it as the exception. Akamai pairs long TTLs with purge on publish where content is rarely changed but consistency matters. Google rate-limits invalidation and calls it a last resort (Akamai, Google Cloud).
- Verify at the edge instead of trusting the configuration: read Cache-Control, Age and the CDN’s cache-status header on a real response. A HIT proves the copy was still fresh enough to serve. It does not prove the TTL was right.
Interactive Animation
Examples
# Set TTL via Cache-Control
Cache-Control: public, max-age=3600 # 1 hour for all caches
Cache-Control: public, s-maxage=86400 # 1 day on CDN, default for browsers
Cache-Control: public, max-age=31536000 # 1 year (immutable assets)
# Nginx: set TTL per location
location /static/ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
location /api/ {
add_header Cache-Control "public, max-age=60, s-maxage=300";
}
# Check remaining TTL from CDN response
$ curl -sI https://cdn.example.com/style.css | grep -i 'cache-control\|age'
Cache-Control: public, max-age=3600
Age: 1247
# Remaining TTL = 3600 - 1247 = 2353 seconds
Frequently Asked Questions
TTL (time to live) is how many seconds a cached response stays fresh before a cache must revalidate or refetch it. Origins set it with Cache-Control max-age, or s-maxage for shared caches such as a CDN edge. It is the main lever on cache hit ratio and origin load.
# Set TTL via Cache-Control
Cache-Control: public, max-age=3600 # 1 hour for all caches
Cache-Control: public, s-maxage=86400 # 1 day on CDN, default for browsers
Cache-Control: public, max-age=31536000 # 1 year (immutable assets)
# Nginx: set TTL per location
location /static/ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
location /api/ {
add_header Cache-Control "public, max-age=60, s-maxage=300";
}
# Check remaining TTL from CDN response
$ curl -sI https://cdn.example.com/style.css | grep -i 'cache-control\|age'
Cache-Control: public, max-age=3600
Age: 1247
# Remaining TTL = 3600 - 1247 = 2353 seconds
Yes. TTL (Time To Live) is also known as Freshness lifetime. TTL (time to live) is how many seconds a cached response stays fresh before a cache must revalidate or refetch it. Origins set it with Cache-Control max-age, or s-maxage for shared caches such as a CDN edge. It is the main lever on cache hit ratio and origin load.
Related CDN concepts include:
- Age Header — The Age response header is a cache's estimate, in seconds, of how long ago the …
- max-age — max-age is a Cache-Control response directive giving the seconds a stored response may be reused …
- s-maxage — A Cache-Control response directive that sets how long a shared cache — a CDN edge, …
- stale-while-revalidate — A Cache-Control response directive (RFC 5861) that lets a cache keep serving a stale stored …
- Cache-Control — Cache-Control is the HTTP header field, defined in RFC 9111, that carries caching directives to …
- ETag — An HTTP response header carrying an entity tag: an opaque validator identifying one version of …