Back to HTTP Headers

Age caching response

How many seconds a cached response has been sitting in a shared cache — tells you how fresh the response still is.

What it does

Age tells you how many seconds a response has been sitting in a shared cache (CDN, proxy) since it was fetched from the origin. It's added by intermediate caches — your origin server never sets it. When you see Age: 0, the cache just fetched a fresh copy. When you see Age: 3600, the cache has been holding this response for an hour.

Combined with Cache-Control: max-age, Age lets you calculate exactly how much freshness is left in a cached response: remaining = max-age - Age.

Syntax

Age: <seconds>

A non-negative integer. No units — always seconds.

Age: 0
Age: 120
Age: 86400

Calculating remaining freshness

Given:

Cache-Control: max-age=3600
Age: 2700

The response has been in cache for 2700 seconds (45 minutes). It has 3600 - 2700 = 900 seconds (15 minutes) of freshness remaining.

Once Age >= max-age, the response is stale. The cache must revalidate before serving it — unless stale-while-revalidate or stale-if-error directives allow serving stale content temporarily.

When Expires is used instead of max-age:

Remaining freshness = Expires - Date - Age

Who sets Age — and who doesn't

Sets Age: CDNs (Cloudflare, Fastly, CloudFront, etc.), caching proxies, Varnish, nginx with proxy caching enabled. Any shared cache that holds and re-serves responses.

Does not set Age: Origin servers. Your application server (Laravel, Express, Django, etc.) should never set Age. If your origin is setting Age, that's a bug or misconfiguration.

Browser cache: Browsers don't add Age to requests — they maintain their own internal freshness tracking and don't expose it.

What Age: 0 actually means

Age: 0 means the cache just fetched a fresh copy from the origin on this request, or the response was generated directly without going through a stale cache entry. It does NOT mean the cache wasn't involved — it means the cache considered the response fresh at time of serving (age was 0 or close enough to round down).

You'll often see Age: 0 on the first request after a cache MISS, before the response is actually cached.

How it interacts with other headers

Age works alongside Cache-Control: max-age to tell you how much of the freshness budget is spent. Without max-age, Age alone doesn't tell you if the response is stale or fresh.

Date is the timestamp when the origin generated the response. Age is accumulated cache time since then. Together: Date + Age ≈ now (the current time, roughly).

If a response passes through multiple caches, each cache adds its own hold time to Age before passing it on. So Age represents total time in any shared cache, not just the last hop.

Debugging with Age

Age is invaluable for debugging CDN behaviour:

  • Age is missing → the response came directly from the origin (CDN MISS, or CDN is bypassed)
  • Age: 0 → CDN just fetched a fresh copy; first hit after a MISS or TTL expiry
  • Age is climbing on every request → CDN is serving from cache (HIT), response is getting older
  • Age resets to 0 unexpectedly → CDN cache was invalidated (purge, deploy), or the response TTL expired and was refreshed
  • Age exceeds max-age → stale response being served (check stale-while-revalidate or misconfiguration)

Common mistakes and gotchas

Mistaking Age: 0 for "not cached." Zero doesn't mean bypassed — it means freshly cached. Look at other cache headers (CF-Cache-Status, X-Cache, X-Served-By) to determine if a CDN was involved.

Ignoring Age when debugging stale content. If users report seeing old content, check Age. If Age is high and max-age is low, you might have stale-while-revalidate serving stale content longer than expected. If Age is very high, check whether your CDN TTL is overriding your Cache-Control.

Setting Age on origin responses. This is a bug — Age should only be added by shared caches. An origin setting Age: 500 gives clients incorrect freshness information.

Real-world examples

CDN serving a cached response (30 minutes old):

HTTP/1.1 200 OK
Cache-Control: public, max-age=3600
Age: 1800
Date: Mon, 22 Jun 2026 10:00:00 GMT
ETag: "abc123"
CF-Cache-Status: HIT

Remaining freshness: 3600 - 1800 = 1800 seconds (30 more minutes).

Fresh CDN response (just cached):

HTTP/1.1 200 OK
Cache-Control: public, max-age=300
Age: 0
CF-Cache-Status: MISS

Stale response being served via stale-while-revalidate:

HTTP/1.1 200 OK
Cache-Control: public, max-age=60, stale-while-revalidate=3600
Age: 500

Age: 500 > max-age: 60, so the response is stale — but stale-while-revalidate: 3600 allows serving stale content for up to 3600 seconds past max-age while revalidating in the background. The response is 440 seconds into its stale-while-revalidate window.

FAQ

Can Age ever decrease between requests?

No — within a cache's lifetime, Age should only increase. If you see Age jump backward, either you hit a different CDN edge node (with a different cache state), the cache was purged and refilled, or something is misconfigured.

What if there's no Age header on a CDN response?

Some CDNs only add Age on cache HITs, not MISSes. Others always add it. Check your CDN's documentation. Absent Age doesn't definitively mean the response bypassed the CDN — it might just mean it was a MISS.

Does Age affect browser caching?

Not directly. Browsers use Age to adjust their own freshness calculation — they subtract Age from max-age to determine how much freshness is left when the response arrives. But browsers don't set or forward Age in requests.

Fun fact

Age was added to HTTP/1.1 to solve a subtle problem with shared caches: a CDN might serve a response with max-age=3600 that it has held for 3599 seconds, giving the receiving client only 1 second of freshness. Without Age, the client would think it had a full hour. With Age, the client knows the response is nearly stale and can decide how to handle it. It's a small header that does important work quietly in the background of almost every CDN-served web page.