Stockio

HTTP Caching Headers: Cache-Control for Fast Assets

Set browser and shared-cache policies for HTML and versioned assets, understand validators and revalidation, and test repeat visits without caching private pages.

By Ethan Clarke

Performance & SEO Specialist ·

On this page

Caching can make repeat navigation dramatically cheaper, but a careless header can also preserve stale HTML or expose personalized content through a shared cache. A reliable website policy distinguishes content that changes at a stable URL from assets whose URLs change whenever their bytes change.

Classify responses before setting lifetimes

Create three initial groups: public HTML, fingerprinted static assets, and personalized responses. Add separate rules where downloads, feeds, or public documents have different update requirements. Cache policy follows content, not merely file extension; a route ending in an image extension might still require authorization.

Use the API latency tester to compare publicly accessible response timings before and after delivery changes. Treat single measurements cautiously and inspect actual headers with browser developer tools or an HTTP client. Timing alone cannot establish whether a browser cache, CDN, or origin served the response.

Learn the distinction between a private browser cache and a shared intermediary from MDN's HTTP caching guide. A response that is safe for one signed-in browser may be unsafe for a CDN to reuse for another visitor. Document personalization explicitly before enabling shared HTML caching.

Include the update contract in the inventory: how often content changes, how quickly a correction must appear, and whether old bytes can coexist with a new deployment. Those answers determine sensible lifetimes better than copying a year-long policy everywhere.

Cache fingerprinted assets aggressively

An asset named with a content-derived fingerprint can receive a long lifetime because changing the file creates a new URL. CSS, JavaScript, and static images often fit this pattern. The critical assumption is URL immutability: never replace the bytes at that same fingerprinted path.

A representative response might include:

HTTP/1.1 200 OK
Content-Type: text/css
Cache-Control: public, max-age=31536000, immutable
ETag: "theme-a17c9"

This is a policy example, not a universal requirement. One year is conventional for immutable assets, but the right choice still depends on URL discipline and delivery constraints. The immutable directive indicates that a fresh response need not be revalidated during normal reload behavior in supporting clients.

Keep older fingerprinted files available long enough for previously delivered HTML to reference them. Deleting all previous assets immediately after deployment can break open tabs, cached pages, and delayed visits. Your release retention policy is therefore part of caching correctness.

Avoid attaching the same long-lived policy to stable names such as a frequently replaced logo file. Either fingerprint the URL or use a shorter lifetime and validators. Query-based versioning can work if every cache layer includes the relevant parameter in its key, but verify that assumption with the actual CDN configuration.

Review Cache-Control directive semantics whenever combining directives. More words in a header do not necessarily create a more precise policy, and conflicting rules are difficult to reason about.

Revalidate HTML without confusing no-cache and no-store

HTML at a stable URL often needs a different policy because it points to the newest assets and content. No-cache permits storage but requires validation before reuse. No-store tells caches not to store the response. Those directives are not synonyms.

For public HTML where freshness must be checked on each reuse, a policy can look like this:

Cache-Control: no-cache
ETag: "page-revision-42"

The browser can send If-None-Match on a later request. If the representation is unchanged, a 304 response avoids retransmitting the full body. The network round trip remains, so revalidation is not equivalent to serving a fresh response directly from local storage.

For sensitive personalized pages, start from an explicit private policy and consider no-store where the privacy requirements call for it. Do not assume the presence of a session cookie automatically prevents every shared cache from storing a response. Review framework defaults and CDN overrides together.

A public page that can tolerate brief staleness may use a short freshness lifetime instead. Choose it from the publishing requirement: an emergency correction page and a static documentation overview do not necessarily need identical behavior. Record the maximum acceptable stale interval before changing headers.

Coordinate browser and CDN behavior

A shared cache can use s-maxage to set its own freshness lifetime independently of the browser's max-age. Some providers also support provider-specific controls. Understand which layer consumes each header and whether it passes the header onward unchanged.

Define the cache key deliberately. Hostname, path, query parameters, cookies, request headers, and content negotiation can affect which representation should be returned. Ignoring a meaningful dimension can serve the wrong language or personalized version. Including every irrelevant dimension can destroy the hit rate.

Use Vary only when the response genuinely changes with the named request header. Vary: Accept-Encoding is common when compressed representations differ. Varying on broad, high-cardinality headers can fragment caches substantially. Inspect MDN's Vary reference before treating it as a general personalization solution.

Separate invalidation from freshness. A CDN purge does not necessarily remove an already fresh response from a visitor's browser cache. This is why fingerprinted assets are safer than relying on emergency purges for changed bytes. Plan how HTML updates propagate before an urgent correction occurs.

If your application uses a service worker, inspect its storage and fetch strategy too. Service-worker caches can produce behavior that does not match the HTTP cache policy alone. Test with the worker active, then isolate it when diagnosing surprising results.

Test cold visits, repeat visits, and deployments

Build a small test sequence: load with an empty cache, revisit normally, reload, then deploy a visible change and revisit again. Record response status, transferred bytes, cache source, Age where available, and the selected representation. Do not rely only on a fast waterfall color.

Use browser tools with cache disabling turned off for repeat-visit tests. DevTools settings that intentionally bypass the cache can make a correct policy appear ineffective. Conversely, a local disk hit says nothing about whether the CDN is configured safely for another user.

Compare anonymous and authenticated requests using separate test sessions. Confirm that one account's content never appears in another response, and avoid using real customer data during these checks. Test error responses too; a cached temporary failure can outlive its underlying cause.

Keep the policy near the development documentation, and revisit it when changing CDNs or publishing workflows. The existing asset-hosting comparison helps explain why independently cacheable resources behave differently from embedded bytes. Add policy checks to the wider performance review rather than treating headers as one-time infrastructure trivia.

Key takeaways

  • Separate immutable assets, public HTML, and personalized pages.
  • Use long lifetimes only when URLs truly identify fixed bytes.
  • Remember that no-cache allows storage but requires validation.
  • Test browser, shared-cache, and service-worker behavior separately.
  • Include deployment retention and urgent updates in the policy.
Featured sponsor

Reach designers & developers at work

Put your product next to the tools people use every day. One sponsor per placement, no tracking pixels.

Become a sponsor

Keep reading

Related articles

More in Performance & SEO
Performance & SEO

10 Best Free Website Speed Test Tools

Find a suitable website speed tester for quick checks, repeatable lab runs, or real-user monitoring, and learn how to interpret the results without chasing a meaningless perfect score.

Includes: API latencyMarcus BellMarcus Bell
Performance & SEO

Monitor Core Web Vitals With Real-User Data

Instrument LCP, INP and CLS with the web-vitals library, collect privacy-conscious RUM events, and turn field percentiles into actionable performance investigations.

Includes: API latencyEthan ClarkeEthan Clarke
Performance & SEO

Critical Rendering Path: Defer JavaScript for Speed

Trace rendering bottlenecks, choose safe script-loading strategies, and verify faster page delivery with a repeatable network and main-thread audit.

Includes: API latencyEthan ClarkeEthan Clarke
Performance & SEO

How to Improve INP and Fix Slow User Interactions

Find what delays clicks, taps, and typing with a practical INP workflow covering field data, browser traces, long tasks, rendering, and regression testing.

Includes: API latencyMarcus BellMarcus Bell
DevOps & Deployment

10 Best Uptime Monitoring Tools

Compare ten uptime monitoring services for websites and APIs, from simple availability checks to incident workflows. Learn what each is suited to, what it cannot tell you, and how to choose.

Includes: API latencyRavi DesaiRavi Desai
Backend & APIs

12 Best Free Public APIs for Side Projects

Find approachable public APIs for weather, books, countries, and sample data, plus practical notes on access, limits, and choosing an endpoint for a side project.

Includes: API latencyChidi NwosuChidi Nwosu