Match Cache Headers to Update Frequency
HTTP caching should follow how often each URL actually changes. MDN and web.dev on Cache-Control, freshness, revalidation, and long-lived fingerprinted assets.
A service site updates its homepage offer copy weekly, ships a new logo once a year, and regenerates hashed CSS on every deploy. If every URL gets the same cache policy, one of two failures appears: visitors keep seeing last month’s headline, or every return visit re-downloads assets that have not changed. HTTP caching is the mechanism that ties storage lifetime to how often a resource actually changes.
MDN’s HTTP caching guide explains freshness, staleness, and revalidation in terms of response age. Google’s web.dev guidance translates that into practical Cache-Control patterns for versioned versus unversioned URLs. For business sites, the useful move is not “cache everything forever,” but matching directives to each class of content.
The problem: one policy for mixed change rates
Without an explicit Cache-Control header, caches may still store and reuse responses through heuristic caching. MDN notes that HTTP is designed to cache as much as possible, and that heuristic reuse—such as treating a long-unchanged Last-Modified resource as reusable for roughly 10% of the time since that modification—is a legacy workaround. Their guidance is blunt: basically all responses should specify Cache-Control explicitly.
web.dev makes the operational risk clear. Leaving Cache-Control out does not disable caching; browsers guess. On a marketing site, that guess can mix an old HTML shell with newer scripts—or force full re-downloads of immutable logo files. A useful analogy is a shared office printer tray: if every document type uses the same “keep for a week” rule, confidential drafts and never-changing letterhead are treated the same, and neither group is served well.
Freshness, staleness, and how revalidation works
MDN defines age as time elapsed since the response was generated. A response with Cache-Control: max-age=604800 stays fresh for one week (604,800 seconds); after that it is stale. Fresh responses can fulfill requests without contacting the origin. Stale responses are not always discarded: clients can revalidate with conditional requests using If-None-Match (paired with ETag) or If-Modified-Since (paired with Last-Modified). If the resource is unchanged, the server can answer 304 Not Modified with almost no body—cheap confirmation rather than a full download.
Key directives, as documented on MDN and summarized on web.dev:
max-age=N— the response remains fresh for N seconds after generation (prefer overExpireswhen both appear).no-cache— do not reuse a stored response without revalidating with the server first (it does not mean “never store”).no-store— do not store the response in any cache.private/public— limit storage to the browser versus allow shared caches (CDNs, proxies).immutable— signal that the representation will not change while fresh (useful with longmax-ageon fingerprinted files).
web.dev’s recommended starting set is compact: no-cache for resources that must revalidate before every use; no-store when nothing should be stored; max-age=31536000 (one year) for versioned resources. Sam Thorogood’s web.dev companion piece “Love your cache” describes a modern default of max-age=0,must-revalidate,public (equivalent in spirit to no-cache) plus CDNs for low-latency checks, then opting into long-lived caches for fingerprinted assets with max-age=31536000,immutable.
| Resource class | Typical change rate | Practical header pattern |
|---|---|---|
| HTML / entry URLs | Frequent or must stay current | Cache-Control: no-cache (+ ETag) |
| Fingerprinted CSS/JS/images | Only via new URL | max-age=31536000 (optionally immutable) |
| Personalized / account pages | Per user | private, no-cache |
| Sensitive one-offs | Must not linger | no-store |
Mechanism for business sites: bust the URL when content changes
The durable pattern MDN calls cache busting (and web.dev calls versioned URLs) embeds a content hash or version in the filename—styles.a1b2c3.css instead of styles.css. When the file changes, the URL changes, so long-lived caches never need a purge for that asset. MDN notes that QPACK’s common values include one week (604800), one month (2592000), and one year with public (31536000), and that in practice the chance a response still sits in cache after a week is already limited—so the critical choice is separating immutable hashed assets from documents that must stay current.
HTML entry points cannot be renamed on every deploy the same way. For those, prefer no-cache with ETag (web.dev prefers ETag over Last-Modified for accuracy) so return visitors revalidate quickly and receive 304 when the page is unchanged. Add private when cookies personalize the response. MDN cautions against liberally using no-store when the real goal is freshness: no-store skips many platform benefits, including smoother back/forward cache behavior; no-cache with private is often the better “always check” posture for personalized pages.
Limits and tradeoffs
Long max-age without fingerprinting locks visitors into stale CSS or logo files until the lifetime expires—there is no standard way for the origin to delete copies already stored in intermediate caches. Conversely, no-cache on every image adds latency on every view; that cost hits people on slower mobile networks hardest, as “Love your cache” notes, even when a 304 keeps the byte count small.
Heuristic caching, kitchen-sink header stacks, and CDN-specific overrides can still surprise teams. Managed caches (CDNs, reverse proxies) may offer purge APIs that go beyond the HTTP caching specification—use those tools deliberately rather than assuming no-store alone cleared every edge node.
Practical takeaways for your next website
- Set an explicit
Cache-Controlon every response class—do not rely on heuristic defaults. - Fingerprint static assets in the build so CSS, JS, and stable images can use long
max-age(up to31536000). - Revalidate HTML with
no-cache(or equivalent) plusETagso offer copy updates without waiting out a long TTL. - Mark personalized responses
privateso shared caches do not serve one client’s page to another. - Reserve
no-storefor genuinely sensitive responses; prefer revalidation when the goal is freshness, not secrecy.
The next step is inventory: list your URL types by how often they change, then assign one header pattern per type instead of a single site-wide guess.
Ready to Discuss Your Website?
If you are planning a new website, a service redesign, or custom development for your business, we’re here to help shape the work.
- Explore Our Services: Web Design · Web Development · Website Redesign
- Start a Conversation: Tell us about your project via our Contact Form or email us directly at
hello@windware.dev.