- Home
- HTTP Headers
- Age Header
Header
Age Header
Learn how the Age header indicates how long a response has been cached in seconds. Understand cache freshness calculations and CDN behavior.
TL;DR: Indicates how long a response has been sitting in a cache (in seconds). Helps determine cache freshness and time until expiration.
What is Age?
The Age header tells you how long (in seconds) a response has been stored in a cache. It’s like a timestamp that says “this content has been sitting in the cache for X seconds.”
When a cache (like a CDN, proxy, or browser cache) serves a stored response, it adds or updates the Age header to indicate the response’s age. This helps clients and intermediaries understand how fresh or stale the cached content is.
How Age Works
First request - cache miss:
GET /api/data HTTP/1.1
Host: api.example.com
```text
**Origin server response:**
```http
HTTP/1.1 200 OK
Cache-Control: max-age=3600
Content-Type: application/json
{"data": "fresh from origin"}
Second request (10 minutes later) - cache hit:
HTTP/1.1 200 OK
Age: 600
Cache-Control: max-age=3600
Content-Type: application/json
{"data": "fresh from origin"}
```text
The Age header shows the response has been cached for 600 seconds (10 minutes).
## Syntax
```http
Age: <delta-seconds>
The value is the number of seconds the response has been in the cache.
# Fresh - just cached
Age: 0
# 5 minutes in cache
Age: 300
# 1 hour in cache
Age: 3600
# 1 day in cache
Age: 86400
```text
## Common Examples
### CDN Cache Hit
```http
HTTP/1.1 200 OK
Age: 1200
Cache-Control: max-age=3600
Content-Type: text/html
X-Cache: HIT
<!DOCTYPE html>...
Content has been in CDN cache for 20 minutes (1200 seconds).
Reverse Proxy Cache
HTTP/1.1 200 OK
Age: 45
Cache-Control: public, max-age=300
Content-Type: application/json
Via: 1.1 varnish
{"data": "value"}
```text
Response has been in Varnish cache for 45 seconds.
### Fresh from Origin
```http
HTTP/1.1 200 OK
Age: 0
Cache-Control: max-age=7200
Content-Type: image/png
[PNG image data]
Response just fetched from origin (Age: 0).
Near Expiration
HTTP/1.1 200 OK
Age: 3500
Cache-Control: max-age=3600
Content-Type: application/json
{"data": "expires soon"}
```text
Response is almost stale (100 seconds until expiration).
## Real-World Scenarios
### CDN with Multiple Cache Layers
**Request through multi-tier cache:**
```http
GET /images/logo.png HTTP/1.1
Host: cdn.example.com
Response from edge cache:
HTTP/1.1 200 OK
Age: 900
Cache-Control: public, max-age=86400
Content-Type: image/png
X-Cache: HIT
X-Cache-Hits: 42
[PNG data]
```javascript
The image has been in the edge cache for 15 minutes, served 42 times.
### API Response Freshness Check
**Client code checking freshness:**
```javascript
fetch('https://api.example.com/data').then((response) => {
const age = parseInt(response.headers.get('Age') || '0')
const maxAge = 3600 // from Cache-Control
const freshnessLifetime = maxAge - age
const freshnessPercent = (freshnessLifetime / maxAge) * 100
console.log(`Content is ${freshnessPercent.toFixed(0)}% fresh`)
console.log(`Expires in ${freshnessLifetime} seconds`)
return response.json()
})
Stale Content Detection
async function fetchWithFreshnessCheck(url) {
const response = await fetch(url)
const age = parseInt(response.headers.get('Age') || '0')
const cacheControl = response.headers.get('Cache-Control')
// Parse max-age from Cache-Control
const maxAgeMatch = cacheControl?.match(/max-age=(\d+)/)
const maxAge = maxAgeMatch ? parseInt(maxAgeMatch[1]) : 0
const isStale = age >= maxAge
if (isStale) {
console.warn('Warning: Serving stale content')
// Trigger background refresh
fetch(url, { cache: 'reload' })
}
return response.json()
}
```javascript
### Cache Performance Monitoring
```javascript
// Track cache age distribution
const cacheAges = []
fetch('https://api.example.com/data').then((response) => {
const age = parseInt(response.headers.get('Age') || '0')
cacheAges.push(age)
// Calculate average cache age
const avgAge = cacheAges.reduce((a, b) => a + b, 0) / cacheAges.length
console.log(`Average cache age: ${avgAge} seconds`)
return response.json()
})
Age Calculation
Single Cache
Age = time_since_cached
Example:
- Cached at: 10:00:00
- Current time: 10:15:00
- Age: 900 seconds (15 minutes)
Multiple Caches (Accumulative)
Age = age_from_previous_cache + time_in_current_cache
Example:
- Origin → CDN Origin Shield: Age: 300
- CDN Origin Shield → Edge: Age: 300 + 60 = 360
- Client receives: Age: 360
With Validation
# If cache validates with origin and content unchanged
Age resets to 0 or continues from validation time
Freshness Calculation
// Calculate remaining freshness lifetime
function getFreshnessLifetime(response) {
const age = parseInt(response.headers.get('Age') || '0')
const cacheControl = response.headers.get('Cache-Control') || ''
// Extract max-age
const maxAgeMatch = cacheControl.match(/max-age=(\d+)/)
const maxAge = maxAgeMatch ? parseInt(maxAgeMatch[1]) : 0
// Extract s-maxage (shared cache max-age)
const sMaxAgeMatch = cacheControl.match(/s-maxage=(\d+)/)
const sMaxAge = sMaxAgeMatch ? parseInt(sMaxAgeMatch[1]) : maxAge
const freshnessLifetime = sMaxAge - age
return {
age,
maxAge: sMaxAge,
freshnessLifetime,
isStale: freshnessLifetime <= 0,
freshnessPercent: Math.max(0, (freshnessLifetime / sMaxAge) * 100)
}
}
// Usage
fetch('https://api.example.com/data').then((response) => {
const freshness = getFreshnessLifetime(response)
console.log(`Age: ${freshness.age}s`)
console.log(`Freshness: ${freshness.freshnessPercent.toFixed(0)}%`)
console.log(`Expires in: ${freshness.freshnessLifetime}s`)
return response.json()
})
```javascript
## Best Practices
### 1. Use Age to Make Informed Decisions
```javascript
// ✅ Check age before using cached data
async function fetchData(url, maxAcceptableAge = 300) {
const response = await fetch(url)
const age = parseInt(response.headers.get('Age') || '0')
if (age > maxAcceptableAge) {
console.warn(`Data is ${age}s old, forcing refresh`)
return fetch(url, { cache: 'reload' })
}
return response
}
2. Monitor Cache Performance
// ✅ Track cache hit rates and age distribution
class CacheMonitor {
constructor() {
this.stats = { hits: 0, misses: 0, ages: [] }
}
recordResponse(response) {
const age = parseInt(response.headers.get('Age') || '0')
if (age === 0) {
this.stats.misses++
} else {
this.stats.hits++
this.stats.ages.push(age)
}
}
getStats() {
const hitRate = this.stats.hits / (this.stats.hits + this.stats.misses)
const avgAge = this.stats.ages.reduce((a, b) => a + b, 0) / this.stats.ages.length
return {
hitRate: (hitRate * 100).toFixed(2) + '%',
avgAge: avgAge.toFixed(0) + 's'
}
}
}
```text
### 3. Set Appropriate Cache-Control
```javascript
// ✅ Server sets proper cache directives
app.get('/api/data', (req, res) => {
res.setHeader('Cache-Control', 'public, max-age=3600')
res.json({ data: 'value' })
})
// CDN or proxy will add Age header automatically
4. Handle Stale-While-Revalidate
// ✅ Serve stale content while revalidating
async function fetchWithSWR(url) {
const response = await fetch(url)
const age = parseInt(response.headers.get('Age') || '0')
const cacheControl = response.headers.get('Cache-Control') || ''
// Check if stale-while-revalidate is allowed
const swrMatch = cacheControl.match(/stale-while-revalidate=(\d+)/)
const swrSeconds = swrMatch ? parseInt(swrMatch[1]) : 0
const maxAgeMatch = cacheControl.match(/max-age=(\d+)/)
const maxAge = maxAgeMatch ? parseInt(maxAgeMatch[1]) : 0
const isStale = age >= maxAge
const canServeStale = age < maxAge + swrSeconds
if (isStale && canServeStale) {
// Serve stale, revalidate in background
fetch(url, { cache: 'reload' })
}
return response.json()
}
```text
## Cache Age Analysis
### Debugging Cache Behavior
```bash
# Check cache age over time
for i in {1..5}; do
echo "Request $i:"
curl -sI https://api.example.com/data | grep -E "Age:|Cache-Control:"
sleep 60
done
# Output shows increasing Age:
# Request 1: Age: 0
# Request 2: Age: 60
# Request 3: Age: 120
# Request 4: Age: 180
# Request 5: Age: 240
Calculating Time to Expiration
function getTimeToExpiration(response) {
const age = parseInt(response.headers.get('Age') || '0')
const cacheControl = response.headers.get('Cache-Control') || ''
const maxAgeMatch = cacheControl.match(/max-age=(\d+)/)
const maxAge = maxAgeMatch ? parseInt(maxAgeMatch[1]) : 0
const secondsUntilExpiration = maxAge - age
if (secondsUntilExpiration <= 0) {
return 'Expired'
}
const minutes = Math.floor(secondsUntilExpiration / 60)
const seconds = secondsUntilExpiration % 60
return `${minutes}m ${seconds}s`
}
```text
## Common Patterns
### Progressive Age Headers
```http
# First cache layer (CDN origin)
Age: 100
# Second cache layer (CDN edge)
Age: 150
# (100 from origin + 50 in edge cache)
# Client browser cache
Age: 150
# (browser uses the received Age value)
Age: 0 Scenarios
# Fresh from origin
Age: 0
Cache-Control: max-age=3600
# Just validated and still fresh
Age: 0
Cache-Control: max-age=3600
# No cache (always 0)
Age: 0
Cache-Control: no-cache
```text
## Testing
### Using curl
```bash
# Check age header
curl -sI https://cdn.example.com/image.png | grep Age
# Multiple requests to see age increase
curl -sI https://api.example.com/data | grep -E "Age:|Date:"
sleep 30
curl -sI https://api.example.com/data | grep -E "Age:|Date:"
# Force cache bypass
curl -sI https://api.example.com/data -H "Cache-Control: no-cache"
Using JavaScript
// Monitor age over multiple requests
async function monitorCacheAge(url, requests = 5, interval = 5000) {
for (let i = 0; i < requests; i++) {
const response = await fetch(url)
const age = response.headers.get('Age')
console.log(`Request ${i + 1}: Age = ${age || '0'}s`)
if (i < requests - 1) {
await new Promise((resolve) => setTimeout(resolve, interval))
}
}
}
monitorCacheAge('https://api.example.com/data')
Related Headers
- Cache-Control - Caching directives and max-age
- Expires - Absolute expiration time
- ETag - Resource version for validation
- Last-Modified - Last modification time
- Vary - Cache key variations
- Via - Proxy and cache chain information
Frequently Asked Questions
What is the Age header?
The Age header indicates how long a response has been in a proxy cache, measured in seconds. It helps clients understand how fresh cached content is.
How is Age calculated?
Age equals the time since the response was generated by the origin server. Each cache adds its residence time. A response with Age: 3600 has been cached for 1 hour.
What does Age: 0 mean?
Age: 0 means the response was just fetched from the origin server or the cache just received it. The content is as fresh as possible from the cache.
How does Age relate to max-age?
If Age exceeds max-age, the cached response is stale. For example, with max-age=3600 and Age: 4000, the response is 400 seconds past its freshness lifetime.