s-maxage
Stands for "shared max-age". Overrides max-age specifically for shared caches (CDNs, reverse proxies). Browser caches ignore this directive and use max-age instead. This allows different TTLs for edge caches vs. end-user browsers.
Full Explanation
s-maxage (short for "shared max-age") overrides max-age specifically for shared caches like CDNs and reverse proxies. Browser caches completely ignore it and stick with max-age. This gives you independent control over how long content lives at the edge versus in the user's browser.
Why would you want different TTLs? Because CDN cache invalidation is something you control (you can purge it), but you cannot purge a user's browser cache. So the common pattern is: keep browser cache short with max-age so users pick up changes quickly, but let CDNs hold content longer with s-maxage to reduce origin load. When you deploy a change, you purge the CDN and browsers will revalidate within their short window.
Cache-Control: public, max-age=60, s-maxage=86400, stale-while-revalidate=3600
This header tells browsers to cache for 60 seconds, lets CDNs cache for 24 hours, and allows CDNs to serve stale content for up to an hour while fetching a fresh copy in the background. That is a pattern you will see on a lot of high-traffic sites. The CDN absorbs almost all traffic, origin load stays low, and users still get reasonably fresh content.
Examples
A practical header for a product listing page where CDN freshness can be managed via purging:
Cache-Control: public, max-age=60, s-maxage=3600
Browser cache expires after 1 minute. The CDN holds it for 1 hour. When prices change, you purge the CDN and browsers pick up the change within 60 seconds.
In nginx, you can set this per location block:
location /products/ {
add_header Cache-Control "public, max-age=60, s-maxage=3600, stale-while-revalidate=600";
}
Frequently Asked Questions
Stands for "shared max-age". Overrides max-age specifically for shared caches (CDNs, reverse proxies). Browser caches ignore this directive and use max-age instead. This allows different TTLs for edge caches vs. end-user browsers.
A practical header for a product listing page where CDN freshness can be managed via purging:
Cache-Control: public, max-age=60, s-maxage=3600
Browser cache expires after 1 minute. The CDN holds it for 1 hour. When prices change, you purge the CDN and browsers pick up the change within 60 seconds.
In nginx, you can set this per location block:
location /products/ {
add_header Cache-Control "public, max-age=60, s-maxage=3600, stale-while-revalidate=600";
}
Related CDN concepts include:
- max-age — Specifies the maximum time in seconds that a response is considered fresh. Applies to all …