Cache-Control is a set of instructions used in HTTP headers to dictate how web browsers and shared caches (like CDNs) store and reuse website files. It defines whether a resource should be saved, how long it remains valid, and when the browser must check the server for updates. Properly configuring these rules ensures your audience sees the most recent content without sacrificing site speed.
What is Cache-Control?
Cache-Control is an HTTP header consisting of key-value pairs called "directives." These directives appear in both server responses and client requests to manage how files—including images, scripts, and HTML—are stored for future use.
The header uses a comma-separated syntax. For example, Cache-Control: max-age=3600, public tells a browser and a CDN that they can store a file for exactly one hour. If a directive requires an argument, it is joined by an equals sign, usually as an integer.
Why Cache-Control matters
Effective caching directly impacts how users experience your site and how search engines crawl it.
- Page speed improvement: Loading resources from a local browser cache is significantly faster than fetching them from a server, which helps meet Core Web Vitals thresholds.
- Reduced server load: Correct headers prevent unnecessary requests to your origin server, saving bandwidth and infrastructure costs.
- Data security: Directives like
privateprevent sensitive user data—such as banking details or login sessions—from being stored in public caches like CDNs. - Content freshness: It allows you to force a browser to check for new versions of dynamic content, ensuring users do not see outdated information.
- Bandwidth conservation: Reusing files locally reduces the total data transferred between the server and the end user.
How Cache-Control works
When a browser requests a page, the server sends back a response. If that response includes a Cache-Control header, the browser follows those specific rules to determine if it should store the file in its "private" local cache.
If a shared cache, such as a CDN, exists between the user and the server, it also reads these headers. It may store a "fresh" copy to serve to other users, reducing the distance the data travels.
The Freshness Lifecycle
- Fresh: The resource is within its allowed lifespan (TTL) and the browser reuses it instantly.
- Stale: The lifespan has expired. The browser cannot use the file as-is and must perform revalidation.
- Revalidation: The browser sends a small request to the server asking if the file has changed. If the server says "no" (a 304 Not Modified response), the browser marks the file as fresh again.
Types of Cache-Control directives
Directives are split between those sent by the server (response) and those sent by the browser (request).
| Directive | Type | Functional Goal |
|---|---|---|
max-age |
Both | Sets the lifespan in seconds (e.g., 3600 for one hour). |
no-cache |
Both | Forces the browser to validate with the server before every use. |
no-store |
Both | Disables all caching; the file must be redownloaded every time. |
public |
Response | Allows both browsers and shared CDNs to store the file. |
private |
Response | Limits caching to the user's browser only; no CDNs allowed. |
s-maxage |
Response | Sets the lifespan specifically for shared caches (CDNs), overriding max-age. |
immutable |
Response | Indicates the file will never change while fresh, stopping reloads. |
Best practices
Use cache-busting for static assets.
For CSS or JavaScript files that change occasionally, include a version hash in the filename. This allows you to set a very long max-age (like one year) because any update to the file will create a new URL, forcing the browser to download the new version immediately.
Apply the 10% rule for heuristic caching.
If you do not specify an expiration time, some caches estimate one based on the Last-Modified header. [A recommended benchmark for this estimate is 10% of the time since the file was last changed] (RFC 9111).
Always specify "private" for personalized content.
If a page displays user-specific data, mark it as private. This prevents a CDN from accidentally serving one user's personal dashboard to another visitor.
Explicitly define rules for dynamic content.
Do not leave caching to chance. For pages that update frequently, use no-cache to ensure the browser always checks for the latest version while still benefiting from 304 Not Modified responses.
Common mistakes
Mistake: Confusing no-cache with no-store.
Fix: Use no-cache if you want the browser to store the file but check for updates before using it. Use no-store only when you want to prevent the file from ever being saved to a disk (e.g., for highly sensitive data).
Mistake: Using max-age=0 to prevent all caching.
Fix: [While browsers historically used max-age=0 as a workaround for older caches that did not support no-cache, it can still allow stale responses if the server is disconnected] (MDN). Use no-cache or no-store for modern reliability.
Mistake: Forgetting the Vary header.
Fix: Use the Vary header alongside Cache-Control when content changes based on the user agent or language. This prevents a mobile user from receiving a cached desktop version of a page.
Examples
Static Resource (Images/CSS)
Cache-Control: public, max-age=31536000, immutable
This tells all caches to store the file for one year and not to check for updates even if the user refreshes the page.
Sensitive Dashboard
Cache-Control: private, no-store
This ensures the browser never saves the file to the disk and a CDN never sees it.
Frequently Updated Homepage
Cache-Control: no-cache
The browser stores the HTML but sends a quick request to the server every time to see if a newer version exists before displaying it.
FAQ
Does Cache-Control affect SEO? Yes, indirectly. Core Web Vitals, specifically Largest Contentful Paint (LCP), are influenced by how fast resources load. Effective caching reduces latency, which can lead to better rankings and a lower bounce rate.
What happens if I have both max-age and Expires?
If both are present, max-age takes precedence. Expires is an older header that uses an absolute date (e.g., Sat, 13 May 2023) whereas max-age uses a relative time in seconds.
Can I clear a remote CDN cache using Cache-Control? No. Cache-Control directives manage future behavior. To clear files already stored on a CDN or intermediate server, you must use that provider's specific "purge" or "invalidation" tool.
What is "revalidation"? Revalidation is a "conditional request." The browser sends a token (like an ETag) to the server. If the version on the servant matches the token, the server sends a 304 status code without the bulk of the file, saving time and data.