A CDN cache is useful only when it serves a response that is appropriate for the next request. That makes caching a content decision before it becomes a speed setting. A public stylesheet, a translated product page, and an account balance have different reuse requirements, even when they travel through the same network.

This guide explains how to express those requirements with HTTP headers and verify the resulting behavior. The examples are starting points for public educational or business websites; choose freshness periods from your own publishing needs. For the surrounding architecture, start with the CDN fundamentals guide.

Start with the response, not the file extension

List what can change the response body: the URL, signed-in identity, language, currency, query parameters, or request headers. A path ending in .json can contain private information. An HTML document can be identical for everyone. Neither the extension nor the HTTP method alone proves that sharing is safe.

Separate your inventory into public, personalized, and sensitive responses. Public content may be eligible for shared storage. Personalized content needs an explicit policy that prevents one visitor's response reaching another. Sensitive content may need to avoid HTTP storage entirely. Record the owner of each decision alongside the route, so a future application change does not silently invalidate the original assumption.

Read the main Cache-Control directives precisely

The following meanings refer to unqualified response directives. The HTTP caching standard, RFC 9111, is the authoritative reference for their semantics.

DirectiveMeaningPractical implication
max-age=300Sets a freshness lifetime of 300 seconds.Age already accumulated upstream matters.
s-maxage=600Overrides max-age or Expires in shared caches.Private browser caches ignore this directive.
no-cacheRequires successful validation before reuse.A stored response can still exist.
no-storeProhibits storing the response in HTTP caches.Use when storage itself is unacceptable.
privateProhibits shared-cache storage.Browser storage remains possible.
publicExplicitly allows shared storage subject to other rules.Use only after establishing that sharing is safe.

The naming trap is no-cache: it does not mean that bytes cannot be saved. Also, s-maxage carries shared-cache revalidation requirements once the response becomes stale. Avoid assuming that an expired shared response can always be served during an outage.

Give browsers and the edge different freshness windows

Suppose a public documentation page can tolerate ten minutes of edge reuse, while you want browsers to check more frequently. After confirming that every visitor receives the same representation, a candidate response is:

Cache-Control: public, max-age=60, s-maxage=600
ETag: "docs-revision-42"

The numbers express a policy, not a promise about when the content will change. A CDN might evict an object early or decline to cache it under its configured eligibility rules. A browser receiving an already aged response does not necessarily receive a new full minute of freshness. Measure the delivered headers, including Age, instead of starting a stopwatch when your browser receives the file.

Apply this policy narrowly. Do not copy it onto signed-in routes, checkout responses, or responses personalized by cookies. Some directives can relax the restrictions surrounding requests with Authorization. Treat any proposal to share authenticated content as a separate design requiring explicit review.

Use validators to avoid unnecessary body transfers

A validator helps answer whether a saved representation is still current. An ETag identifies a representation version. A later conditional request can carry that value in If-None-Match. When the selected representation is unchanged, the server can respond with 304 Not Modified rather than sending the body again.

GET /guides/caching/ HTTP/1.1
If-None-Match: "docs-revision-42"

This still involves a network exchange. Validation can save bandwidth without eliminating the latency of contacting the validating server. Ensure the validator changes when the representation changes, and test the actual generated response rather than assuming a framework supplies correct tags. Reusing the same tag after editing content can preserve an outdated body despite apparently successful validation.

Make the cache key match the representation

A cache key determines which saved response a request can find. The URL is central, but request headers can also affect selection. The Vary response header declares which request headers influenced the representation. For example, Vary: Accept-Encoding distinguishes relevant content-encoding variants.

If an application negotiates language through Accept-Language, its caching design must preserve that distinction. Explicit paths such as /en/guides/ and /fr/guides/ can make the contract easier to inspect. Either design requires the origin and edge to agree about representation selection. Verify how your provider supports Vary, normalization, and custom cache keys.

Avoid adding every request header to the key. More combinations can create many copies with little reuse. Conversely, ignoring a meaningful query parameter can mix distinct content. Never treat Vary: Cookie as a universal privacy solution: authentication checks, bypass rules, and explicit storage directives still need a deliberate design.

Choose a policy for each content family

Versioned public assets

For a public asset whose URL changes whenever its bytes change, consider a long freshness window with immutable. The response promises that its content will not change while fresh. A filename such as app.7d39c2.js makes that promise easier to maintain. Retain older files while existing documents can still reference them, and publish new files before updating their references.

Public documents that change in place

Choose a short explicit freshness period or require validation, depending on acceptable delay and origin capacity. Keep validators accurate and connect publication to a documented cache invalidation process. A shorter lifetime reduces potential stale exposure but can increase origin work. Estimate that tradeoff using your busiest routes.

Personal and sensitive responses

If local HTTP caching is acceptable but shared storage is not, a policy such as private, no-cache permits private storage with validation before reuse. When the response must not be stored, use no-store. Configure the CDN to bypass these routes too. Cookies alone do not establish a safe caching policy. Coordinate this work with your application and CDN security controls.

Verify the policy through the full delivery path

Start with a small route set and an explicit expected result for each one. Inspect the response status, headers, body version, and provider cache status. Send two equivalent requests, then change one meaningful input at a time. Confirm that different languages stay separate and that anonymous, signed-in, and signed-out responses cannot cross between sessions.

Use a normal browser visit to observe real caching, then a separate diagnostic session to investigate misses. A browser's disabled-cache setting changes the experiment, so record whether it was enabled. Inspect the CDN response as well as the browser's local result; a local cache hit does not prove anything about the edge.

Check configuration precedence. An origin may emit correct headers while an edge rule overrides freshness or eligibility. Review route matching in order, especially broad rules introduced for static assets. Keep a representative private endpoint in regression checks whenever delivery settings change, because a successful public asset test says nothing about that boundary.

Changing a header at the origin affects responses fetched later; it cannot reach a still-fresh copy being reused without a request. When tightening an existing policy, pair the header change with a cleanup plan for managed caches and account for browser copies. This matters particularly after discovering that a route is more personalized than its original implementation suggested.

Test publication and rollback, not just the initial hit. Update a small public document, check validation, purge the expected object, and confirm its content from more than one relevant region when possible. Test error responses separately: an old cached error can hide a successful origin fix. Save representative headers and expected bodies with your delivery implementation notes.

Measure useful reuse

Track cache hits alongside origin requests, bytes transferred, response latency, and correctness incidents. A higher aggregate hit rate is unhelpful if it comes from caching the wrong response. Review route groups independently: a public asset library and a personalized API have different goals. Increase freshness periods only where content ownership, invalidation, and monitoring make the additional reuse dependable.

Conclusion

A reliable caching policy answers four questions: who may reuse this response, how long it stays fresh, what distinguishes its variants, and how it changes. Express those decisions explicitly, verify them at every relevant layer, and keep private data outside shared caches. Faster delivery then follows from reuse that your application can safely support.