Last reviewed: 2026-10-21
Direct answer
Prevent shared-cache data leaks in a coding-agent patch by deciding which responses may be shared before changing cache headers or keys. Keep personalized and protected responses out of shared caches unless a separately reviewed design preserves their access rules. For public responses, every input that changes the reusable representation must either distinguish the cache entry or be removed from the origin’s decision. Test the result through the actual cache with two users and an anonymous client.
The key review question is: can two requests select the same stored response even though they are entitled to different content? An origin authorization check cannot protect a request answered entirely by an edge cache. Similarly, a cache key that ignores a query parameter is unsafe when the origin uses that parameter to select the response.
RFC 9111
defines cache storage, reuse, authenticated-request restrictions, and the Vary mechanism. Its rules also make an important distinction: no-cache permits storage but requires successful validation before reuse; no-store prohibits storage under ordinary HTTP caching semantics. Neither term means that an already distributed response has been recalled.
Who this is for
Use this guide when a coding agent changes CDN rules, reverse-proxy caching, response middleware, static-page generation, a framework’s response cache, or headers such as Cache-Control and Vary. Include patches described as performance work: removing a query parameter from a key or broadening a cacheable route can change a security boundary.
The running example has a public catalog and a signed-in account summary. Catalog variants may be shared when their content is identical for the relevant requests. Account summaries contain synthetic user-specific markers and must never cross between users. The policy and tests below are recommendations for that example, not results from a deployed application.
Key takeaways
- Separate permission to store a response from permission to reuse it for another request.
- Choose an explicit policy for personalized pages, public variants, redirects, and errors.
- Review the cache key and the origin’s input handling together.
- Check every cache layer; a correct origin header does not prove a configured CDN or application cache obeys it.
- Warm the cache before testing isolation, and verify that the positive control actually produces a cache hit.
- Repair existing entries as well as future response headers when a sharing policy changes.
Contract details to verify
Write the sharing policy first
For each changed route, record the allowed audience, response variants, cache layers, storage policy, freshness policy, and invalidation mechanism. Treat the body and security-relevant response headers as part of the representation being reviewed. A shared redirect to a user-specific destination can be as wrong as a shared account body.
| Example response | Intended contract | Review decision |
|---|---|---|
| Public catalog in a supported currency | Shared within the selected public variant | Currency selection must agree between key and origin |
| Signed-in account summary | Available only to its authorized caller | Bypass shared storage and reuse; enforce origin authorization |
| Sensitive downloadable export | No HTTP cache storage under the example policy | Send no-store and verify managed-cache bypass |
| Login redirect or account error | No reuse across callers | Inspect response middleware and status-specific cache rules |
Use the site’s object-authorization workflow to establish who may read the underlying object. Then verify that a cache cannot answer a later caller outside that policy. Cache isolation supplements the authorization review.
Select directives by their actual meaning
MDN’s HTTP caching guide
explains that cookies alone do not make a response private and recommends private for personalized responses. Avoid treating the presence of a session cookie, Set-Cookie, or a login requirement as a substitute for an explicit policy.
| Directive | Meaning relevant to this review |
|---|---|
private without a field-name argument | A shared HTTP cache must not store the response; a private cache may store it subject to other rules |
no-cache without a field-name argument | Storage is permitted, but reuse requires successful validation |
no-store | Ordinary HTTP caches must not store the response; verify separate managed-cache behavior |
public | Explicitly permits caching, including some responses otherwise restricted because of request authorization |
s-maxage | Sets shared-cache freshness and can permit shared caching of an authorized request’s response under RFC rules |
For a personalized page where browser storage is acceptable but revalidation is required, Cache-Control: private, no-cache expresses that policy. For the example’s sensitive export, use Cache-Control: no-store and an explicit bypass at any managed cache that can override origin directives. These are different product decisions; do not apply either indiscriminately to all assets.
RFC 9111 Section 3.5 lists must-revalidate, public, and s-maxage as directives that can allow shared caching of responses to requests containing Authorization, subject to their requirements. Therefore, adding a common performance-header helper to an authenticated route deserves direct review. The absence of a directive is also not a reliable instruction to disable caching: some responses are heuristically cacheable.
MDN notes that managed caches can implement explicit behavior beyond ordinary HTTP caching. Inspect the effective CDN rules, reverse-proxy configuration, worker code, and application-cache implementation. Set the account route to bypass both shared lookup and insertion under the example policy. Setting only an origin response header cannot prevent a previously stored entry from being served before the origin receives the request.
Make key selection agree with origin behavior
Apply this invariant to each response eligible for sharing: requests that select the same entry must be allowed to receive the same cached representation under the route’s reuse policy. List every response-changing input: path, query parameters, selected request headers, host, negotiated language, and any cookie-driven behavior.
For a catalog whose currency query parameter changes displayed prices, omitting currency from the key while forwarding it to the origin breaks that invariant. Either include the supported currency variant in the effective key, redesign the URL, or stop sharing that response. A tracking parameter may be omitted only after verifying that it cannot change the reusable body or headers. The parameter’s name is not evidence of irrelevance.
Cloudflare’s cache-key documentation shows that ignoring query strings can make otherwise different URLs share a key. It also recommends aligning URL normalization with the URL sent to the origin to prevent cache poisoning. Review how percent-encoding, duplicate parameters, parameter ordering, and path normalization are interpreted on both sides. Preserve meaningful distinctions; reject ambiguous forms when the route contract does not support them.
For header-based public variants, RFC 9111 Section 4.1 describes how Vary nominates request fields used to distinguish stored responses. Verify the actual cache’s handling rather than assuming every CDN implements arbitrary Vary fields through the same mechanism. Cloudflare documents explicit key settings and plan-dependent availability. If the deployed product cannot represent the required distinction, keep that route outside shared caching.
Do not solve personalized caching by copying raw access tokens or complete session cookies into a custom key. The example deliberately bypasses those requests. Review any alternative user-scoped cache as a separate design with trusted identity, authorization and revocation rules, bounded cardinality, and protected diagnostics. A caller-controlled tenant header cannot establish its own authorization scope.
Also inspect unkeyed inputs that influence public output. For example, an application may construct an absolute link from a forwarded host field. If the cache does not distinguish that input, one request can influence what later visitors receive. Accept such routing metadata only through the deployment’s trusted proxy contract, or remove it from response construction. Adding every arbitrary header to the key can create excessive variants without fixing the trust problem.
Happy-path operator workflow
- Freeze the proposed revision and inventory changed cache headers, middleware, routes, CDN rules, and application-cache calls.
- Write the sharing table and enumerate the inputs that affect each reusable response.
- Compare the effective key with the exact request the origin receives, including normalization and trusted proxy metadata.
- Configure shared bypass for the account and export paths; choose deliberate browser-storage and revalidation policies.
- Start a disposable test origin and cache using the reviewed configuration. Give two synthetic accounts distinct response markers and separate client cookie jars.
- Prove public caching works with an origin request counter, then run the isolation matrix below against cold and warm entries.
- Exercise revalidation, errors, and concurrent requests. Retain the revision, rule version, fixture labels, origin counts, and sanitized results.
- Before promotion, account for objects stored under the previous policy and verify the required invalidation or key migration.
Inspect request parsing separately with the site’s HTTP framing review . Consistent cache keys do not repair parser disagreement between intermediaries.
Prove isolation with a warm-cache test matrix
Use harmless labels such as account-A and account-B instead of real account data. Run both fill orders because the first response can determine what later requests see. These are proposed integration assertions; no application test execution is claimed.
| Fixture or sequence | Required observation |
|---|---|
| Two identical public catalog requests | Correct public content; a confirmed reusable hit reduces origin requests |
| Public currency A, then currency B, then A again | Each response has the intended currency; variants never collide |
| Account A, account B, then anonymous at the same account URL | No response contains another principal’s marker; anonymous access is denied |
| Anonymous request first, then signed-in accounts | A cached redirect or denial cannot replace the authorized response |
| Account A and B arrive together on a cold entry | Request collapsing never distributes one personalized result to both callers |
| A supposedly irrelevant query parameter changes | Content and relevant headers remain invariant, or the policy distinguishes or bypasses it |
| Alternate encodings, duplicate parameters, and normalized paths | Key and origin retain the same meaning or reject the unsupported form |
| Benign untrusted header marker reaches the public route | It cannot change another client’s cached body, links, or security-relevant headers |
| Revalidation, expiry, and configured stale-response behavior | The same audience and variant restrictions remain effective |
| Error, redirect, and deployment from an old warm cache | Status-specific rules and old objects cannot reintroduce a forbidden response |
Do not rely on a HIT label alone. Correlate the returned marker, origin counter, rule revision, and cache diagnostics. An Age value is useful evidence, but its absence alone does not establish that no application or managed cache was involved. Conversely, a test where every response misses may prove little about the sharing behavior the patch intends to introduce.
Use synchronization barriers for simultaneous requests rather than an arbitrary sleep. Keep each principal’s browser or HTTP-client state separate. A reused cookie jar can make the harness itself change identity, while a random query value on every request can accidentally prevent the cache collision being tested.
Error-path operator workflow
If a response crosses principals or variants, block the patch and preserve a minimal synthetic sequence tied to the exact revision and cache configuration. Disable shared lookup and insertion for the affected route while repairing its contract. Do not recover by widening key exclusions or treating a foreign marker as an acceptable stale response.
Changing future headers does not remove old objects. MDN explicitly notes that no-store does not delete an already stored response for the URL. Use the deployed cache’s supported invalidation mechanism or a reviewed key-namespace migration, and verify the formerly warm request cannot retrieve the old object. Account for every managed layer. A browser copy already delivered to a user cannot be recalled by purging the CDN.
Record invalidation identity and outcome without exposing bodies, credentials, cookies, or sensitive cache-key values. If invalidation is uncertain, keep the bypass in place and reconcile that operation before restoring shared service. A suspected production exposure belongs in the service’s incident process; a passing repaired test does not establish what earlier visitors received.
Failure modes
no-cache is read as no storage. It requires validation before ordinary reuse. Choose private or no-store according to the storage policy and verify managed-cache configuration.
Only the origin route is tested. A shared hit can skip it. Exercise the actual cache and assert both content and origin activity.
A key ignores input the origin still uses. Keep the distinction, remove that input’s effect, or bypass sharing. Do not optimize the hit rate before proving equivalent responses.
Only successful responses receive privacy headers. Redirects and some negative responses can be cached. Review middleware order and status-specific rules.
The fix ships with old public entries intact. Purge or migrate them and verify warm-cache behavior before declaring the policy effective.
FAQ
Does Vary: Cookie replace a private-response policy?
MDN recommends private for personalized responses rather than using a cookie-based Vary rule as the sharing boundary. The example also bypasses shared lookup and insertion. A deliberately user-scoped cache needs its own authorization and revocation design.
Is no-store a privacy guarantee?
No. RFC 9111 says it is insufficient to ensure privacy against malicious or compromised caches. It also does not recall prior copies. Review transport, access controls, managed-cache behavior, and retained objects separately.
Must caching be disabled everywhere?
No. Public resources with an explicit variant and freshness contract can remain shared. The acceptance gate is correct reuse and preserved isolation, not a globally low hit rate.
Sources checked
- RFC Editor: RFC 9111, HTTP Caching
— fetched successfully on 2026-10-21. The June 2022 standard supports cache keys, storage, authorized requests,
Vary, directives, request collapsing, and cache-poisoning considerations. It is a stable normative reference for this evergreen review. - MDN: HTTP caching — fetched successfully on 2026-10-21. Supports private versus shared caching, cookie caveats, directive distinctions, managed-cache overrides, request collapsing, and the limits of changing headers after storage. Rechecked as maintained technical guidance, not a new security announcement.
- Cloudflare: Cache keys — fetched successfully on 2026-10-21; the page displays a 2026-09-29 update. Supports configurable query/header/cookie key fields, normalization alignment, and plan-dependent availability. Its implementation details apply to Cloudflare; other caches require their own configuration review.
Reader next step
Choose one cache-affecting coding-agent patch. Write the route’s sharing policy, compare its effective key with origin inputs, and run a public hit control followed by account A, account B, and anonymous requests against the same warm path. Keep promotion blocked until the returned representations respect that policy and the deployment plan accounts for entries stored under the previous configuration.