Table of Contents
1. Why Caching Matters So Much
One of the core principles of web performance optimization: the fastest request is no request at all.
HTTP caching is the cornerstone of web performance. A well-designed caching strategy can reduce page load time by more than 50%, while significantly lowering server load and bandwidth costs. According to HTTP Archive data, an average page has 70% of its requests being cacheable static resources—images, CSS, JS, and font files. If these resources were re-downloaded every time, it would be a huge waste of bandwidth and backend resources.
But caching is also a double-edged sword. Improper configuration can cause users to see stale content, or after updating a JS file, users still use the old version, triggering all sorts of bizarre bugs. This article will systematically explain the complete knowledge system of HTTP caching from both frontend and operations perspectives.
2. The Two Major Types of HTTP Caching
HTTP caching is divided into two major categories: strong caching and negotiated caching. The core difference lies in whether a request needs to be sent to the server to verify whether the cache is still valid.
2.1 Strong Caching
Strong caching means: during the cache period, the browser doesn’t send a request at all and directly uses the local cache.
The HTTP response headers that implement strong caching are Cache-Control (HTTP/1.1) and Expires (HTTP/1.0).
HTTP/1.1 200 OKCache-Control: max-age=31536000, immutablemax-age=<seconds>: The maximum valid time of the cache (in seconds), calculated from the request timeimmutable: Tells the browser that this resource will never change, and even if the user presses F5 to refresh, it should not re-validate
Expires vs Cache-Control
Expires uses absolute time, which has the problem of clock desynchronization:
Expires: Wed, 21 Oct 2026 07:28:00 GMTIf Cache-Control and Expires both exist, Cache-Control takes higher priority. In modern web development, we should uniformly use Cache-Control.
2.2 Negotiated Caching
After strong caching expires, the browser sends a request with a validator to the server, and the server determines whether the resource has changed. If it hasn’t changed, it returns 304 Not Modified, and the browser continues to use the local cache; if it has changed, it returns 200 OK and the new content.
Negotiated caching relies on two pairs of HTTP headers:
Last-Modified / If-Modified-Since
# Response headersLast-Modified: Tue, 01 Jul 2026 10:30:00 GMT
# Subsequent request headersIf-Modified-Since: Tue, 01 Jul 2026 10:30:00 GMTThe server compares the file’s last modification time. The drawback is that the precision is only at the second level, and if the file content doesn’t change but the mtime does (such as after a redeployment), it will also cause cache invalidation.
ETag / If-None-Match
# Response headersETag: "abc123"
# Subsequent request headersIf-None-Match: "abc123"ETag is a hash value of the resource content, which is more precise than Last-Modified. If the content doesn’t change, the ETag doesn’t change. ETag has higher priority than Last-Modified.
3. Detailed Explanation of Cache-Control Directives
Cache-Control is the core of caching strategy. Let’s break down the meaning of each directive one by one:
| Directive | Purpose | Typical Scenario |
|---|---|---|
public | Any node can cache (browser, CDN, proxy) | Public static resources |
private | Only the browser can cache | User personal data |
no-cache | Can be cached, but must be validated before each use | Pages that need timely updates |
no-store | No caching at all | Sensitive data (banking pages) |
max-age=N | Cache valid for N seconds | All resources |
s-maxage=N | Cache time for CDN/proxy only | CDN caching strategy |
must-revalidate | Must validate with server after expiration | Important resources |
stale-while-revalidate | Can use stale cache after expiration, update asynchronously in background | Improve response speed |
stale-if-error | Can use stale cache when server errors occur | Disaster recovery and degradation |
immutable | Resource never changes, no validation request initiated | Static resources with fingerprints |
4. In Practice: Caching Strategies for Different Resources
4.1 Static Resources with Hashes (JS/CSS/Images)
This is the type of resource most suited for aggressive caching. The content hash in the filename (such as main.a1b2c3d.js) ensures that the URL changes when the file content changes:
Cache-Control: public, max-age=31536000, immutablemax-age=31536000: Cache for one yearimmutable: Even F5 refresh won’t re-validate, because URL change = content change
Modern bundlers like Vite, Webpack, and Next.js all output hashed filenames by default.
4.2 HTML Pages
HTML cannot be aggressively cached because it is the entry point of the application. Once cached, users won’t see updates:
Cache-Control: no-cacheOr use a shorter max-age:
Cache-Control: public, max-age=0, must-revalidateno-cache and max-age=0, must-revalidate have similar effects—validate every time, but allow returning 304.
4.3 API Endpoints
For REST API responses, the caching strategy depends on the real-time requirements of the data:
// User personal info: do not cacheres.setHeader('Cache-Control', 'private, no-cache, no-store');
// Public article list: can cache for 60 secondsres.setHeader('Cache-Control', 'public, max-age=60');
// Nearly unchanging data (e.g. country list): cache for 1 dayres.setHeader('Cache-Control', 'public, max-age=86400');4.4 Font Files
Font files are large in size and rarely change, making them suitable for long-term caching:
Cache-Control: public, max-age=31536000, immutableBut cross-origin issues need attention: browsers enforce CORS checks on font files, so make sure the CDN is configured with Access-Control-Allow-Origin.
4.5 Service Worker Scripts
Service Worker caching has special rules: browsers ignore Cache-Control and cache for at most 24 hours. This is a browser security mechanism to ensure that malicious SWs don’t persist permanently.
5. CDN Caching Strategies
Modern web applications almost all use CDNs, and the CDN layer has its own caching logic.
5.1 CDN Cache Keys
CDNs distinguish different resources based on cache keys. The default cache key is 域名 + URI. But for multilingual sites or A/B testing, custom cache keys may be needed:
#Add Accept-Language to the cache key to enable per-language cachingCache-Key: ${host}${uri}${http_accept_language}5.2 CDN Purge
When resources need to be urgently updated, there are two options:
- Purge: Immediately delete the CDN cache, and the next request goes back to origin
- Prefetch: Push resources to CDN edge nodes in advance
Purge operations have a cooldown period (varies by CDN provider) and cannot be called frequently.
5.3 Coordinating Origin and CDN Cache Times
A common question is: what happens when the origin’s Cache-Control: max-age=3600 conflicts with the cache time set in the CDN console?
Conclusion: Most CDNs prioritize the origin’s Cache-Control. But you can use s-maxage to separately control the CDN layer’s cache time:
Cache-Control: public, max-age=3600, s-maxage=86400This means the browser caches for 1 hour, and the CDN caches for 24 hours.
6. Cache Invalidation Strategies
Cache updates are one of the hardest problems. The industry mainly has the following strategies:
6.1 File Fingerprinting
The best practice. Each build artifact’s filename contains a hash of its content:
main.abc123.js → main.def456.jsOld files can continue to be cached, and new files are treated as new resources because of their different URLs. This is why Webpack/Vite output [name].[contenthash].js by default.
6.2 Cache Busting: Query Parameters
Append ?v=1.0.1 to the URL:
<link rel="stylesheet" href="/style.css?v=2.0.0">This approach is simple but has many problems:
- Many CDNs ignore query parameters by default, causing busting to fail
- Intermediate proxies may not cache resources with query parameters
- Cannot be used in conjunction with
immutable
Not recommended; filename fingerprinting should be preferred.
6.3 Purge API
Proactively clear the cache through the API provided by the CDN. Suitable for emergency fix scenarios (such as production bugs), but not suitable as a regular update method.
7. Debugging and Verification
7.1 Chrome DevTools
In the Network panel, check the Size column of resources:
(disk cache)/(memory cache): Strong cache hit304 Not Modified: Negotiated cache hit200 OK(具体大小): Re-downloaded from the server
Note: The Disable cache checkbox only takes effect while DevTools is open. Don’t forget to uncheck it to verify actual caching behavior.
7.2 cURL Command-Line Debugging
# View full response headerscurl -I https://example.com/style.css
# Send If-None-Match to verify conditional cachingcurl -I -H 'If-None-Match: "abc123"' https://example.com/style.css
# Simulate browser caching behaviorcurl -H 'Cache-Control: max-age=0' https://example.com/style.css7.3 Troubleshooting Common Issues
| Symptom | Possible Cause | Investigation Direction |
|---|---|---|
| Users see old version after resource update | HTML is cached | Check the HTML’s Cache-Control |
| Re-downloaded on every refresh | Missing strong cache headers | Add max-age and immutable to static resources |
| CDN caching not working | CDN ignored response headers | Check CDN console configuration |
| All resources re-validated on F5 refresh | immutable not added | Add immutable to hashed resources |
8. Best Practices Summary
- Use filename fingerprinting + long-term caching for static assets:
Cache-Control: public, max-age=31536000, immutable - Use
no-cacheor a shortmax-agefor HTML entry points: Ensure users get the latest page structure - Cache APIs in tiers based on data real-time requirements: Don’t cache user data; public data can be briefly cached
- Use
s-maxageto differentiate CDN cache control: CDNs can cache longer than browsers - Don’t purge all caches on deployment: Under a filename fingerprinting strategy, old assets and old HTML are compatible
- Leverage
stale-while-revalidateandstale-if-errorto improve user experience and availability
There is no silver bullet for caching strategies. The key is to choose the right strategy based on an asset’s change frequency and users’ real-time requirements. Once you understand the underlying mechanisms of HTTP caching, you can confidently make these trade-offs.