The Cache-Control header is the HTTP instruction that tells browsers, CDNs, and intermediate proxies how to store a resource and how long they can reuse it before requesting it from the server again. Configuring it correctly can reduce load time on repeat visits to near zero for static assets.
This article explains the available directives, how to choose the right one for each resource type, and how to apply it in Apache, Nginx, or directly from PHP.
What Cache-Control Is and Why It Matters
When a browser first visits your site it downloads everything: HTML, CSS, JavaScript, images, fonts. Without caching, on the second visit it downloads all of them again. With a correct caching policy, it only downloads what changed.
Cache-Control replaced older headers like Expires and Pragma: no-cache. It is the modern, precise way to control caching behavior in HTTP/1.1 and later.
A poor caching policy has two failure modes: a very short cache time wastes bandwidth and slows the site; a very long time causes users to see outdated CSS or JavaScript after a deploy. The trick is in the strategy.
Key Cache-Control Directives
The header accepts one or more comma-separated directives. Here are the most relevant ones:
| Directive | Effect |
|---|---|
max-age=N |
Resource is valid for N seconds from when it was downloaded. |
s-maxage=N |
Like max-age but only for shared caches (CDN, proxy). |
no-cache |
Store in cache but revalidate with server before serving. |
no-store |
Do not store the resource in any type of cache. |
public |
Allows CDNs and proxies to share the response between users. |
private |
Only the user's browser may store the response (not CDNs). |
immutable |
Resource will never change; browser skips revalidation during max-age. |
stale-while-revalidate=N |
Serves the cached resource while updating it in the background. |
The most common combination for fingerprinted static assets
Cache-Control: public, max-age=31536000, immutable
One year of caching (max-age=31536000) plus immutable tells the browser never to revalidate. This works as long as the filename includes a content hash (fingerprinting): app.a3f2c1.js. On deploy, the hash changes, the URL changes, and the browser downloads the new version.
For HTML and dynamic responses
Cache-Control: no-cache
no-cache does not mean "don't cache" — that's no-store. no-cache means "cache it but revalidate before serving." The browser sends a conditional request (If-None-Match or If-Modified-Since) and, if the resource hasn't changed, the server responds with a 304 with no body — much faster than downloading again.
How to Configure Cache-Control in Apache (.htaccess)
The most straightforward method in Apache is using mod_headers or mod_expires. The following block covers the most common cases:
<IfModule mod_headers.c>
# Static assets with hash in filename
<FilesMatch "\.(js|css|woff2?|ttf|eot)$">
Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>
# Images and SVG
<FilesMatch "\.(png|jpg|jpeg|gif|webp|svg|ico)$">
Header set Cache-Control "public, max-age=2592000"
</FilesMatch>
# HTML
<FilesMatch "\.html?$">
Header set Cache-Control "no-cache"
</FilesMatch>
</IfModule>
If you use cPanel, this block goes in the .htaccess at your domain's document root. Make sure mod_headers is active — on most shared hosting plans it is enabled by default.
How to Configure Cache-Control in Nginx
In Nginx the equivalent is the add_header directive inside location blocks:
location ~* \.(js|css|woff2?)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
location ~* \.(png|jpg|jpeg|webp|gif|svg|ico)$ {
add_header Cache-Control "public, max-age=2592000";
}
location ~* \.html?$ {
add_header Cache-Control "no-cache";
}
How to Send Cache-Control from PHP
For dynamic responses you want to cache (for example, an API returning data that changes every hour):
<?php
// Cacheable for 1 hour, both browser and CDN
header('Cache-Control: public, max-age=3600, s-maxage=3600');
// Sensitive user data — private cache only, never CDN
header('Cache-Control: private, no-store');
?>
Call header() before any output. Once bytes have been sent to the client, headers can no longer be modified.
For a broader view of optimization techniques, check out our web performance section with additional resources.
Key takeaways
- Cache-Control is the modern HTTP header that controls how and for how long browsers and CDNs store resources.
- For fingerprinted static assets, use
public, max-age=31536000, immutable— the hash in the filename ensures users always download the current version on deploy. no-cache≠ "don't cache": it means "revalidate before serving."no-storeis "don't cache at all."- HTML and dynamic pages should use
no-cacheto prevent users from seeing stale versions. - You can configure Cache-Control in Apache (
.htaccess), Nginx (locationblocks), or directly in PHP withheader().
Is your site still reloading static resources on every visit? The team at elenlace.com sets up optimized caching policies tailored to your stack and verifies they work correctly across both the server and CDN.
FAQ
What is the difference between Cache-Control and Expires?
Expires is the legacy header that specifies an absolute expiry date. Cache-Control: max-age specifies a relative duration and takes priority when both are present. Today, using only Cache-Control is the recommended approach.
Does Cache-Control affect Google rankings?
Not directly, but it impacts performance metrics like LCP and TTFB on repeat visits, which are part of the page experience signals. A good caching policy improves real user speed and, consequently, can improve Core Web Vitals scores.
How do I verify that Cache-Control is working?
Open Chrome DevTools → Network tab → reload the page and inspect the response headers of each resource. Well-cached resources will show "(from disk cache)" or "(from memory cache)" in the Size column on subsequent visits.
Should I use Cache-Control on login or checkout pages?
For pages with personal or session data, use Cache-Control: private, no-store. This prevents intermediate proxies or CDNs from storing responses containing user information, avoiding data leaks between sessions.
Prefer it done for you? El Enlace handles hosting and professional web development.
Compare providers
Other providers and guides worth comparing: