Cache-Control is an HTTP header controlling browser and CDN caching. For immutable assets (JS/CSS/images with hash) — public, max-age=31536000, immutable (1 year). For HTML — public, max-age=60, stale-while-revalidate=86400. For APIs — no-store. Correct config speeds up repeat visits by 30-80%.
Below: step-by-step, working examples, common pitfalls, FAQ.
Cache-Control: public, max-age=31536000, immutableCache-Control: public, max-age=60, s-maxage=600Cache-Control: no-store (personal data) or private, max-age=0, must-revalidate| Scenario | Config / Record |
|---|---|
| nginx: static with hash (1 year) | location ~* \.(js|css|png|jpg|woff2)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
} |
| nginx: HTML (short cache) | location / {
add_header Cache-Control "public, max-age=60, s-maxage=600";
} |
| Apache: .htaccess static | <FilesMatch "\.(js|css|png|jpg|woff2)$">
Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch> |
| API (no-cache) | Cache-Control: no-store, no-cache, must-revalidate |
| CDN-aware (CDN caches, browser does not) | Cache-Control: public, max-age=0, s-maxage=86400 |
Cache-Control: no-cache ≠ "don't cache". It means "always revalidate". Use no-store<code>max-age</code> — TTL for private caches (browser). <code>s-maxage</code> — TTL for shared caches (CDN, proxy). If both are set — each cache uses its own.
A Cache-Control directive: "this resource will never change". The browser skips revalidation (If-None-Match) even on reload. Important for hash-based assets.
Yes. Cache-Control says "cache is valid for N seconds". ETag is used at expiry for 304 Not Modified (conditional requests). They complement each other.
<a href="/en/check">Enterno HTTP Checker</a> → enter URL → Cache section shows Cache-Control, ETag, Age, max-age.