The problem and decision criteria

IXC Insights Editorial · Technical review date: September 26, 2026

Imagine enabling CDN caching to accelerate a product page and showing a signed-in customer another customer's contract price. In this hypothetical design, responses were reused because the product URL matched, although prices differed by customer. The first question is which requests may receive the same response, rather than the cache hit ratio.

This article proposes the following starting point. Treat public static assets and identical public content as shared-cache candidates; initially exclude responses containing personal information, tenant-specific data or authentication state. Assess public prices, inventory and search after defining acceptable information delays and the conditions that change the response. Dynamic generation alone does not make a response uncacheable. HTTP caching concerns storage and reuse conditions; whether a file is static does not determine safety.[1]

A useful operational question is whether requests sharing a cache key are interchangeable in access rights, body, response headers and acceptable freshness. If that is difficult to explain, start by separating responses or disabling shared caching instead of increasing the TTL.

Browser storage and sharing across users are different

A browser's private cache and a CDN's shared cache serve different scopes. When reading Cache-Control, distinguish “may it be stored?” from “when may a stored response be reused?” The following describes common response directives without field-name qualifications.[1]

Response directiveMeaning for storage and reuseCommon misunderstanding
privateA shared cache must not store it; a private cache may.It does not prohibit browser storage.
no-storeInstructs private and shared HTTP caches not to store this request and response.It is not a remote command to delete every existing copy.
no-cacheStorage is possible, but reuse requires successful revalidation with the origin.Despite its name, it does not prohibit storage.

max-age defines the period considered fresh. s-maxage applies to shared caches and overrides max-age there. Freshness expiration is different from immediate physical deletion. An expired object may be revalidated or reused under certain conditions, so a TTL of zero alone should not be interpreted as a ban on storage.[3]

These distinctions concern HTTP caches. Server-side data caches, application-held screen state and browser history require separate review. The HTTP standard also distinguishes history mechanisms from caches. A single header therefore does not complete a requirement to clear a screen after logout.[1]

How the service works

Shape delivery around the content — Set caching by update behavior and observe edge delivery alongside origin load.

Start with a policy table for each response

The table is an editorial design example, not a customer case or execution result. Numbers are assumptions for comparison, not recommended optimal values. The basic scope is read-only GET responses. Do not apply it unchanged to state-changing requests such as creating or updating orders.

The common starting point for keys is the request method and target URL. Verify the actual CDN's key construction and normalization separately. “CDN 30 seconds” is a freshness-policy target, not a guarantee that an object remains stored throughout that period.[1]

Response typeSharing and browser policyVariation conditions and keyTTL and freshness assumptionsInvalidation or transition triggerDesign during origin failure
Versioned public JS, CSS and imagesSharing and browser storage allowedURL containing content version; necessary representation formatCDN and browser: 30 daysNew URL on change; separately block or invalidate security withdrawalsConsider up to 24 extra hours after expiry for the same version only if not withdrawn
Public articles and lists identical for anonymous usersConditional sharing; browser revalidates before reuseActual variants such as language, page and public filtersCDN: 60 secondsInvalidate related lists on edits, deletion or private transitionsNo extra serving after expiry
Login processing, token issuance and account-specific redirectsExclude shared caching; browser no-storeDepends on session and authentication state; do not combine into a common objectDo not storeSession creation, replacement or termination; remove any old cached copiesDo not substitute a success response or another session's response
Personal orders, profiles and payment methodsExclude shared caching; browser no-storeAuthenticated user and object-access permissionDo not storeLogout, permission changes or data updatesError or retry guidance instead of stale personal responses
Tenant dashboards and contract pricesExclude shared caching by default; browser no-storeVerified tenant, user, role and object scopeDo not storeTenant transfer, role or contract changeDo not bypass with a response from previous permissions
Publicly visible display pricesConditional sharing; browser revalidates before reuseProduct, currency, sales market and price-display conditionsCDN: 30 secondsPrice publication or promotion changeNo extra serving after expiry; recheck authoritative price at order confirmation
Public inventory informationConditional sharing; browser revalidates before reuseProduct, public region and inventory query unitCDN: 5 secondsStock movements, sellout or sales suspensionShow unavailable after expiry; do not use for reservation or deduction decisions
Anonymous public search resultsConditional sharing; browser revalidates before reuseSearch term, filters, sort, page and languageCDN: 10 secondsIndex changes or public-access changesNo extra serving after expiry; separately exclude personalized or sensitive searches

Login screens also need distinctions. A common shell with no session information and public images may follow the public-content policy. If the same screen contains a one-time token, username or account-specific redirect, do not combine it under one policy merely because it is called a login page.

For prices and inventory, agree separately on delays acceptable for display and delays acceptable for transaction decisions. The example shares display queries only. It does not delegate order confirmation or stock deduction to cached query results. Once customer-specific discounts are involved, move the response from the public-price row to the personal or tenant-specific row.

Even a response just stored at the CDN is not current business information if the application built it from an old data snapshot. Record origin-data updates, asynchronous propagation and invalidation delays alongside the CDN TTL. If short TTLs are unsupported or propagation cannot meet requirements, retain the option to exclude that response from caching.

Derive cache keys from the reasons responses change

A cache key identifies stored responses. Vary is the standard mechanism for identifying request headers that affect response selection.[2] Trace not only the URL, but also which queries, languages, currencies, cookies, authentication states and tenant identifiers actually change the response.

For example, if the application reads a currency cookie to change prices while the cache distinguishes only URLs, the key omits the need for different prices at the same address. Conversely, including every tracking value that does not affect the response is not the first choice either. Verify that the origin and CDN interpret the same request consistently, then retain the necessary variations.

Values forwarded to the origin need not be the same as values included in the cache key. CloudFront forwards values included in its cache policy, but a separate origin request policy can also forward values absent from the key.[4] Seeing a tenant header arrive at the server therefore does not establish tenant-specific cache separation.

Adding a tenant identifier to the key does not replace authorization. That is why the baseline here excludes tenant responses from shared caches. A separate authenticated shared-cache design must at least apply verified access context before serving a HIT and handle role differences within a tenant and permission revocation. Do not finish the review after trusting a caller-supplied tenant string or adding a token to the key.

Check the product's current support for Vary

Cloudflare announced Vary support in Cache Rules on July 2, 2026. Current documentation describes enabling it in a cache rule and returning Vary from the origin. Neither unconditionally applying older statements that Cloudflare ignores Vary nor assuming that adding the header automatically separates responses in every configuration is correct.[5][6]

Whether to distinguish selected headers literally, normalize them or bypass caching must match origin response generation. When normalizing language or format values in particular, confirm that merged requests may actually receive the same representation. Feature availability and correct application in the current deployment are separate facts.

Look for CDN settings that override origin headers

Seeing Cache-Control: private in a browser does not establish the CDN's internal storage policy. These are examples of different configuration components checked against official documentation on September 26, 2026. They are neither a performance comparison nor equivalent configurations across two products.

Configuration to inspectCaveat in official documentationWhat to check before changing it
Edge TTL in Cloudflare Cache RulesAn option ignores origin Cache-Control and uses the specified TTL.[7]Actual match scope, origin-header handling, later rules and edge code
CloudFront cache policyA Minimum TTL greater than zero can cache for that period despite origin no-cache, no-store or private.[4]All three TTL values and the cache key in the policy attached to the path
CloudFront managed CachingOptimizedMinimum TTL is one second; cookies and query strings are excluded from the key.[8]Whether it is attached too broadly to personalized HTML or APIs

CloudFront explains that setting Minimum, Default and Maximum TTL to zero disables caching. The managed CachingDisabled policy also sets all three to zero.[4][8] After disabling caching, separately verify that necessary request information, such as authentication, still reaches the origin through the appropriate policy.

For Cloudflare, inspect response-stage changes as well as request-stage changes. Cache Response Rules can modify headers such as Cache-Control or Set-Cookie before a response enters the cache.[5] This is why you should inspect client-visible headers, headers used for the caching decision and the final matching rules together.

This table is not a copy-and-paste production configuration. Do not turn it into a global rule before checking the account plan, rule order, path associations and other caching layers.

Authentication headers and cookies are not universal safeguards

RFC 9111 restricts shared-cache reuse of requests with Authorization, but also defines exceptions subject to response directives including public, s-maxage and must-revalidate. An authentication header therefore does not establish that a response can never be shared under any configuration.[1]

Consider cookies separately. CloudFront may ignore cookies or incorporate selected ones into the key, depending on configuration. Its documentation explains that forwarding cookies to the origin can also cause the origin's Set-Cookie to be cached with an object and returned in later responses.[9] Check whether session-issuing responses enter the cache and whether the same session setting is replayed to another user.

Cloudflare's authentication and cookie handling also depends on Origin Cache Control and cache rules.[3] Instead of treating a product name as proof of safety, inspect requests and responses before login, immediately after login and immediately after logout separately.

Comparing response bodies alone is insufficient. Identical screen content may still have different Set-Cookie, account-specific Location or access-related metadata. Extend policy tables and test comparisons to bodies, status codes and security-related headers.

How the service works

Inspect and protect requests before they reach the service — Review request and blocking records, tune protection rules and manage certificates and access controls.

Preserve access boundaries during revalidation and failures

Revalidation with ETag checks whether the same representation changed; it does not decide access permission. RFC 9110 requires normal request checks before evaluating conditional requests.[2] In test design, verify that an old ETag does not cause a revoked user's request to receive 304 Not Modified without an appropriate access decision.

Record stale-response policy separately. stale-while-revalidate serves an expired response while checking for a new one; stale-if-error serves expired content during origin errors.[10] Public articles may accept some delay for availability, but that does not imply that revoked permissions or old contract prices may also persist.

If the table says “no extra serving after expiry,” verify that the actual CDN's error and revalidation policies implement that requirement. CloudFront's expiration documentation also describes circumstances where stale objects can be returned after origin connection failure, depending on settings.[10] Normal-state TTL checks do not establish failure behavior.

Avoid indiscriminately combining directives that require revalidation with directives that allow stale responses. Shared-cache s-maxage also includes proxy-revalidate semantics, so its relationship with stale-serving settings needs review. Check conflicts and precedence in the selected product's documentation and express the expected result as a failure-test acceptance condition.[3]

Changing headers alone does not clear incorrectly stored responses

If a response that must not be cached was stored, handle future policy and already-stored objects separately. The following is a proposed defensive recovery procedure for a scoped environment you own or are explicitly authorized to operate.

  1. Stop incorrect reuse and reinsertion first. Bypass shared caching on affected paths, correct origin response policy and verify rule application. If the origin keeps supplying incorrect objects, they can be stored again after a purge.
  2. Invalidate old keys and variants too. Identify current and former keys, language, query and cookie variants, and the scope of every active cache layer. Cloudflare single-URL purges have limitations with header- and cookie-based custom keys; evaluate API requests that match the real key or another appropriately scoped purge method.[11]
  3. Handle copies outside the CDN and exposure separately. CloudFront invalidation cannot remove browser or corporate-proxy copies.[12] Review application caches, screen state and information already delivered, and connect the findings to the necessary incident response.
  4. Verify recovery through content and access results. A Cloudflare purge HTTP 200 acknowledges the request; it does not prove an object existed or was removed.[13] Recheck current body versions and user isolation. A valid object may already have been populated by another request and return a HIT, so a MISS alone should not define recovery success.

Versioned URLs are useful for deploying public static assets. They do not remove sensitive responses exposed at old URLs or replace access-revocation procedures. Keep file versioning and CDN invalidation responsibilities separate.[12]

Test normal and failure paths with two users

The following is an unexecuted test design example. In an authorized test environment, prepare synthetic users A and B, different synthetic tenants and synthetic order data. Include separate roles to test permission differences within one tenant. Use identifying markers instead of real personal information and separate browser profiles and cookie stores.

CheckRequest sequence and conditionsAcceptance condition
Normal public-cache reuseRepeatedly request the same public asset and populate the cache.Valid reuse is observed on allowed paths with matching content and versions; blocking everything is not success.
User and tenant isolationRequest the same API URL A → B and B → A; also test concurrent requests.Each receives only permitted data, with no other user's marker in body or headers. Sensitive paths are not reused through shared caches.
Login transitions and session issuanceBrowse anonymously, log in and repeat with a fresh B session.No replay of another session's Set-Cookie or account-specific redirect.
Role differences within a tenantRequest the same object with administrator and restricted-role conditions.Even within one tenant, unauthorized fields and objects are not delivered.
Permission revocation and tenant transferChange access while a previous response could still be fresh, then request again, including conditional requests.Old bodies or inappropriate 304 responses do not preserve old permissions; legitimate operations remain available.
Logout and account switchingDistinguish direct requests after logout, back navigation and access to the same screen after B logs in.Neither HTTP responses nor application screen state reveal the previous account's information.
Query, language and currencyChange one response-affecting condition at a time and compare normalized values.Responses that must differ stay separate; only compatible requests receive the same representation.
Expiration and revalidationCompare before and after expiry, with both changed and unchanged origin content.Approved freshness and revalidation policy holds; representations and validators do not mix.
Price or inventory changes and purgeUpdate origin values and inspect related detail, list and search responses; prepare objects under former keys too.Old values do not persist beyond the allowed scope; order confirmation and stock deduction use authoritative state.
Origin errors and timeoutsProduce controlled origin errors and connection failures; combine cache presence and expiry conditions.Sensitive responses are not replaced with stale objects. Public responses stay within the agreed post-expiry allowance.
Error and redirect responsesTest account-specific redirects and 401, 403 and 404 paths as well as success responses.Another user's state is not reused; error pages and headers contain no sensitive data.

Record cache status, response code, Cache-Control, Vary, Age, ETag, body version, synthetic markers and applied policy version together. Real tokens and session cookies need not be retained in evidence files. Correlate user results with origin and CDN records while excluding secrets.

Cache-status names may differ by product and request path. One MISS does not prove “not stored,” and one 200 does not prove “the correct user's data.” The criterion is whether expected results hold across different users, populated caches, expiry, invalidation and origin failures.

When reviewing delivery, origin and transition considerations together, use the CDN and WAF configuration checklist and attach user-isolation results separately.

Cache only what you can operate responsibly

If you cache only public static assets and have clear owners for responses, deployment and invalidation, you can start with a small policy table and regression checks. When tenant-specific responses, multiple CDN, proxy and application caches, and frequent permission changes coexist, owners of each layer must agree on access and freshness criteria. Excluding ambiguous paths is reasonable even if it lowers the initial hit ratio.

The result of a cache review is not simply “caching enabled.” It is a policy linking shareable responses, separating keys, acceptable delays, invalidation triggers, failure behavior and the checks that demonstrate them.

Protecting origin access, separately from shared-cache data boundaries, is covered in Why an origin remains exposed behind a CDN and WAF, and how to protect it.

If considering external support, review the scope of IXC's CDN service and discuss which cache-policy, invalidation and verification work needs review.