Browser Caching: How to Set Cache Headers Correctly in 2027
To set up browser caching, configure `Cache-Control` headers on your server/CDN. Use `max-age=31536000, immutable` for versioned static assets (CSS, JS, images), `max-age=3600, must-revalidate` for HTML pages, and `no-store` for sensitive/dynamic ...
Browser Caching: How to Set Cache Headers Correctly in 2027
Quick answer: To set up browser caching, configureCache-Controlheaders on your server/CDN. Usemax-age=31536000, immutablefor versioned static assets (CSS, JS, images),max-age=3600, must-revalidatefor HTML pages, andno-storefor sensitive/dynamic content. AddETagorLast-Modifiedfor conditional requests. Usestale-while-revalidatefor background updates. Proper caching reduces repeat-visit load times by 50-90%.[1][3][4]
Browser caching is one of the simplest yet most effective performance optimizations. By storing static assets locally, repeat visits load instantly without re-downloading resources. Yet many sites misconfigure cache headers, either caching too little (slow repeat visits) or too much (stale content).
Cache-Control header
The Cache-Control HTTP header controls how browsers and CDNs cache responses.
Directives
| Directive | Meaning | Use case |
|---|---|---|
max-age=N | Cache for N seconds | All cacheable content |
s-maxage=N | CDN cache for N seconds (overrides max-age for shared caches) | CDN-specific caching |
public | Any cache can store (browser + CDN) | Static assets |
private | Only browser can cache (not CDN) | User-specific content |
no-cache | Must revalidate before using cached copy | Frequently updated content |
no-store | Don't cache at all | Sensitive data |
must-revalidate | Must revalidate when expired | HTML pages |
immutable | Content never changes (skip revalidation) | Versioned assets |
stale-while-revalidate=N | Serve stale while fetching fresh in background | Non-critical updates |
Caching strategies by content type
Static assets (CSS, JS, images, fonts)
Cache-Control: public, max-age=31536000, immutable- Cache for 1 year (max recommended)
immutabletells browser not to revalidate (file won't change)- Use content hashing in filenames:
styles.a3f2b1.css - When content changes, filename changes, so old cache is irrelevant[3]
HTML pages
Cache-Control: public, max-age=3600, must-revalidate- Cache for 1 hour
must-revalidateforces revalidation when expired- Or use
stale-while-revalidatefor better UX:
Cache-Control: public, max-age=3600, stale-while-revalidate=86400- Serve stale content for up to 24 hours while fetching fresh in background[3]
API responses
Cache-Control: public, max-age=300, s-maxage=600- Browser caches for 5 minutes
- CDN caches for 10 minutes
- Use
Vary: Accept-Encodingfor content negotiation[3]
Sensitive content (user data, auth pages)
Cache-Control: private, no-store- Don't cache at all
- Prevents shared caches (CDN) from storing user-specific data
- Ensures fresh data on every request[3]
User-specific content (dashboards, profiles)
Cache-Control: private, max-age=300- Only browser caches (not CDN)
- Short cache duration (5 minutes)
- Revalidate on navigation[3]
Conditional requests
ETag
Server sends an ETag (entity tag) with the response:
ETag: "a3f2b1c4d5e6"On subsequent requests, browser sends:
If-None-Match: "a3f2b1c4d5e6"If content hasn't changed, server responds with 304 Not Modified (no body, saves bandwidth).[3]
Last-Modified
Server sends:
Last-Modified: Wed, 04 Nov 2026 12:00:00 GMTBrowser sends:
If-Modified-Since: Wed, 04 Nov 2026 12:00:00 GMTServer responds with 304 if not modified.[3]
Recommendation: Use ETag (more precise) for static assets. Use Last-Modified for dynamic content. Don't use both (redundant).[3]
Service Worker caching
For PWAs, use Service Workers for advanced caching:
// Cache-first strategy (for static assets)
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request);
})
);
});
// Network-first strategy (for API calls)
self.addEventListener('fetch', (event) => {
event.respondWith(
fetch(event.request).then((response) => {
const clone = response.clone();
caches.open('api-cache').then((cache) => {
cache.put(event.request, clone);
});
return response;
}).catch(() => {
return caches.match(event.request);
})
);
});Strategies:
- Cache-first: Serve from cache, fetch if not cached (static assets)
- Network-first: Fetch from network, fall back to cache (API calls)
- Stale-while-revalidate: Serve from cache, update in background (HTML)[3]
Common mistakes
1. No cache headers
Default behavior varies by server. Without explicit headers, browsers may cache unpredictably or not at all.[3]
2. Caching everything
Caching HTML pages for 1 year means users see stale content. Use short cache durations for dynamic content.[3]
3. Not using content hashing
Without content hashing, you can't use long cache durations for updated files. Always hash filenames for static assets.[3]
4. Ignoring Vary header
If your server returns different content based on Accept-Encoding or User-Agent, use Vary header to prevent cache confusion:
Vary: Accept-Encoding5. Caching authenticated responses
Never cache responses that contain user-specific data with public. Use private or no-store.[3]
Measuring cache effectiveness
Chrome DevTools
- Network tab: Check "Size" column (should show "(disk cache)" or "(memory cache)" for cached resources)
- Application tab: View cached resources in "Cache Storage" and "Service Workers"[3]
Lighthouse
- "Serve static assets with an efficient cache policy" audit
- Flags resources with cache duration < 1 year[4]
WebPageTest
- Shows cache hit/miss for each resource
- "Repeat View" metrics show cached performance[4]
Frequently Asked Questions (FAQ)
What is browser caching?
Browser caching stores copies of web resources (images, CSS, JS) on the user's device. On repeat visits, the browser loads these from local storage instead of re-downloading, dramatically reducing load times.[3][4]
How long should I cache static assets?
1 year (max-age=31536000) for versioned static assets (with content hashing in filenames). This is the maximum recommended by Google. Use immutable to prevent revalidation.[3]
What is the difference between no-cache and no-store?
no-cache means the browser must revalidate with the server before using the cached copy (but can still cache). no-store means don't cache at all. Use no-store for sensitive data.[3]
What is stale-while-revalidate?
stale-while-revalidate allows serving stale (expired) content while fetching fresh content in the background. This improves perceived performance — users see content instantly while the cache updates.[3]
How do I check my cache headers?
Use Chrome DevTools Network tab (click a request → Headers tab → Response Headers). Or use curl: curl -I https://example.com/style.css. Look for Cache-Control header.[3]
Should I use ETag or Last-Modified?
Use ETag for static assets (more precise). Use Last-Modified for dynamic content. Don't use both — they're redundant and can cause conflicts.[3]
How does caching affect Core Web Vitals?
Caching improves LCP (cached CSS/JS loads faster), FCP (cached resources render quicker), and reduces server load (lower TTFB). Repeat visits can be 50-90% faster with proper caching.[3][4]
What is a Service Worker?
A Service Worker is a JavaScript file that runs in the background and can intercept network requests. It enables advanced caching strategies (cache-first, network-first, stale-while-revalidate) and offline support for PWAs.[3]
Related reading on MintSaaS
- Core Web Vitals and Image Optimization: LCP, CLS, INP Guide
- How to Use CDN for Faster Website Delivery
- JavaScript Bundle Optimization: Code Splitting Guide
Sources
[1] MDN Web Docs — HTTP Caching: https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching
[2] Web.dev — HTTP cache: https://web.dev/articles/http-cache
[3] MintSaaS Image Compressor: https://www.mintsaas.net/products/image-compressor
[4] Web.dev — Core Web Vitals: https://web.dev/vitals/
[5] MintSaaS PDF Compressor: https://www.mintsaas.net/products/pdf-compressor
Free tools on MintSaaS
Try our free tools to put this guide into practice:
MintSaaS Dispatch articles are researched and synthesized with the assistance of artificial intelligence analysis, then edited, verified, and benchmarked by our engineering editorial team. Customer personal data and platform source files are strictly segregated and are never used to train or fine-tune public artificial intelligence models.
More from MintSaaS Dispatch
Handcrafted technical insights and developer tools.