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 directive | Meaning for storage and reuse | Common misunderstanding |
|---|---|---|
private | A shared cache must not store it; a private cache may. | It does not prohibit browser storage. |
no-store | Instructs private and shared HTTP caches not to store this request and response. | It is not a remote command to delete every existing copy. |
no-cache | Storage 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]
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 type | Sharing and browser policy | Variation conditions and key | TTL and freshness assumptions | Invalidation or transition trigger | Design during origin failure |
|---|---|---|---|---|---|
| Versioned public JS, CSS and images | Sharing and browser storage allowed | URL containing content version; necessary representation format | CDN and browser: 30 days | New URL on change; separately block or invalidate security withdrawals | Consider up to 24 extra hours after expiry for the same version only if not withdrawn |
| Public articles and lists identical for anonymous users | Conditional sharing; browser revalidates before reuse | Actual variants such as language, page and public filters | CDN: 60 seconds | Invalidate related lists on edits, deletion or private transitions | No extra serving after expiry |
| Login processing, token issuance and account-specific redirects | Exclude shared caching; browser no-store | Depends on session and authentication state; do not combine into a common object | Do not store | Session creation, replacement or termination; remove any old cached copies | Do not substitute a success response or another session's response |
| Personal orders, profiles and payment methods | Exclude shared caching; browser no-store | Authenticated user and object-access permission | Do not store | Logout, permission changes or data updates | Error or retry guidance instead of stale personal responses |
| Tenant dashboards and contract prices | Exclude shared caching by default; browser no-store | Verified tenant, user, role and object scope | Do not store | Tenant transfer, role or contract change | Do not bypass with a response from previous permissions |
| Publicly visible display prices | Conditional sharing; browser revalidates before reuse | Product, currency, sales market and price-display conditions | CDN: 30 seconds | Price publication or promotion change | No extra serving after expiry; recheck authoritative price at order confirmation |
| Public inventory information | Conditional sharing; browser revalidates before reuse | Product, public region and inventory query unit | CDN: 5 seconds | Stock movements, sellout or sales suspension | Show unavailable after expiry; do not use for reservation or deduction decisions |
| Anonymous public search results | Conditional sharing; browser revalidates before reuse | Search term, filters, sort, page and language | CDN: 10 seconds | Index changes or public-access changes | No 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 inspect | Caveat in official documentation | What to check before changing it |
|---|---|---|
| Edge TTL in Cloudflare Cache Rules | An option ignores origin Cache-Control and uses the specified TTL.[7] | Actual match scope, origin-header handling, later rules and edge code |
| CloudFront cache policy | A 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 CachingOptimized | Minimum 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.
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.
- 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.
- 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]
- 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.
- 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.
| Check | Request sequence and conditions | Acceptance condition |
|---|---|---|
| Normal public-cache reuse | Repeatedly 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 isolation | Request 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 issuance | Browse 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 tenant | Request the same object with administrator and restricted-role conditions. | Even within one tenant, unauthorized fields and objects are not delivered. |
| Permission revocation and tenant transfer | Change 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 switching | Distinguish 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 currency | Change 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 revalidation | Compare 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 purge | Update 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 timeouts | Produce 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 responses | Test 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.



