Web Cache Security Behind CDNs: Poisoning, Deception, and Cache-Key Mistakes

Understand how CDN and origin disagreements lead to cache poisoning, web cache deception, tenant leakage, and unsafe cache-key decisions.

Back to Blog

Web Cache Security Behind CDNs: Poisoning, Deception, and Cache-Key Mistakes

A CDN can make a website faster while also creating a second decision-maker about what content is reusable. That matters for security. The origin may generate a private response correctly, but a cache can accidentally serve it to another user. A proxy may ignore a header that changes the response. A path normalization difference can let an attacker make a cache store sensitive content under what looks like a static URL.

Web cache security is not one vulnerability. It is a family of failures caused by disagreement between the browser, CDN, reverse proxy, framework, and application about what a request means and whether the response can be shared.

Start with the cache key

A cache key determines which requests are considered equivalent for reuse. A simple cache might key on scheme, host, and path. More complex configurations include selected query parameters, headers, device classes, or cookies.

If the origin changes its response based on an input that the cache does not include in the key, two semantically different requests can collapse into one cache entry.

For example, imagine the origin uses a header to build an absolute URL:

GET /reset-help HTTP/1.1
Host: example.com
X-Forwarded-Host: attacker.example

If the response embeds that host but the CDN does not include the header in its cache key, a poisoned response could be stored and later served to ordinary visitors. The exact exploitability depends on the stack, but the principle is consistent: every input that materially changes a cacheable response must be accounted for or removed from the response-generation decision.

Cache poisoning stores attacker-influenced content

Web cache poisoning happens when an attacker can influence a response that a shared cache then serves to other users. The input may be a header, query parameter, path variant, or host-related value that the origin trusts but the cache does not distinguish.

The defensive task is not to add every possible input to the cache key. That can destroy cache efficiency and create another class of inconsistencies. Instead, identify which request components legitimately affect the response, normalize them consistently, and reject or ignore untrusted components that should not control generated content.

Cache deception makes private content look cacheable

Cache deception takes a different route. The attacker crafts a URL that the application interprets as a dynamic authenticated page while the cache interprets it as a static resource.

A conceptual example is:

https://example.com/account/profile.css

A badly configured application might route that request to the authenticated profile handler because it ignores the fake suffix. A CDN might see .css and cache the response as a public static asset. If a logged-in user is induced to request that URL, their private response could be stored for others.

The fix is consistency. The application, CDN, and web server should agree on path parsing, static file rules, normalization, and which responses are cacheable.

Do not cache sensitive responses by default

OWASP recommends preventing caching of sensitive responses. Responses containing personalized account data, authorization decisions, tokens, private documents, or tenant-specific information should normally use directives appropriate to their sensitivity, such as:

Cache-Control: no-store

private and other directives have their own semantics under HTTP caching rules. Choose them deliberately. If the content must never be stored by shared or private caches, no-store is the clearer instruction.

Do not rely only on URL naming conventions. A route called /api/account is not automatically private to a CDN, and a route ending in .js is not automatically safe to share.

Authorization state must not leak through shared cache

A dangerous pattern is a response that differs based on an authorization header or session cookie while the shared cache ignores that distinction. If authenticated and unauthenticated requests share the same cache key, a private response can be served publicly or a public response can mask a privileged one.

Review which headers and cookies the CDN uses to bypass or vary caching. Be careful with rules that strip cookies before forwarding, cache API responses by path only, or classify every GET request as safe to share.

Multi-tenant applications need tenant-aware cache keys

A multi-tenant SaaS application can leak data even when user authentication is correct if the cache key does not include the tenant dimension that changes the response.

Suppose the origin identifies tenants using a validated host header or a tenant ID after authentication, but the CDN caches only by path:

tenant-a.example.com/report
tenant-b.example.com/report

If the host is not part of the effective cache key, those requests are not safely interchangeable. Similar problems occur when a custom header or path prefix selects a tenant but the cache ignores it.

Cache design should follow the same isolation model as the application.

Normalization mismatches are security relevant

CDNs and frameworks can disagree about encoded characters, repeated slashes, dot segments, semicolons, case sensitivity, and file extensions. An attacker looks for a request that the cache classifies one way and the origin classifies another.

Normalize paths consistently before cache classification. Avoid having the CDN make security-sensitive decisions based on a superficial suffix while the origin performs flexible route matching.

Use Vary carefully

The HTTP Vary response header tells caches that selected request headers affect the representation. It can be appropriate for content negotiation or controlled variants, but it is not a universal fix for cache poisoning.

Do not add attacker-controlled headers to Vary without considering cache fragmentation and resource consumption. The better fix may be to stop that header from changing the response at all.

Make cache policy part of the application contract

Security reviews often inspect application authorization and CDN configuration separately. Cache safety is strongest when the application explicitly declares which routes may be cached and the CDN follows that contract.

Useful patterns include:

  • default private behavior for authenticated routes;
  • explicit cache rules for known public assets and public pages;
  • no-store on security-sensitive responses;
  • clear surrogate or CDN cache controls where the platform supports them;
  • tests that compare CDN behavior with direct-origin behavior.

Watch for stale security state

A cache can preserve more than page content. Stale redirects, old authorization metadata, obsolete CORS responses, or outdated security headers can survive a deployment if cache invalidation is incomplete.

After changing security-sensitive edge behavior, verify the public response from more than one path or region when practical. Purging an origin cache is not necessarily the same as purging the CDN edge.

Debug CDN and origin disagreement deliberately

When investigating a suspected cache problem, compare:

  • the public CDN response;
  • the direct-origin response, if safely accessible for administrators;
  • cache-related response headers such as age or provider-specific cache status;
  • the request headers that reach the origin;
  • the configured cache key and bypass rules;
  • behavior with and without authentication.

A difference is not automatically a vulnerability. It is evidence that the layers are making different decisions, which is where the investigation should focus.

What Vulnify can check around caching

Vulnify's WAF / CDN Detector can help identify public edge infrastructure, and the Security Headers Analyzer can show the public response headers. The Redirect Chain Checker is useful when edge redirects are part of the behavior.

A public scanner cannot infer a complete CDN cache key or prove that tenant-specific authenticated responses are isolated. Those checks require configuration review and controlled testing with authorized accounts.

Cache security review checklist

  • Document which routes are intentionally cacheable.
  • Use Cache-Control: no-store for responses that must not be stored.
  • Confirm authenticated pages are not entering a shared cache unexpectedly.
  • List every request input that changes a cacheable response.
  • Make the cache key match the application's real representation model.
  • Normalize paths consistently across CDN, proxy, and application.
  • Avoid extension-only rules that can misclassify dynamic routes.
  • Include tenant boundaries in cache behavior where responses differ by tenant.
  • Reject untrusted host or forwarding headers that should not influence content.
  • Test invalidation after security-sensitive changes.
  • Compare public edge responses with the expected origin behavior.
  • Log cache status during incident investigation.

Retest cache behavior after authentication and routing changes

Cache safety can regress even when nobody edits the CDN. A framework upgrade may change response headers. A new middleware layer may remove Cache-Control. An authentication redesign may move tenant context from the hostname into a header that the existing cache key ignores.

Include cache tests in releases that change sessions, routing, tenant selection, edge middleware, or CDN configuration. Use two controlled accounts and alternate requests to the same logical route. Confirm that responses do not cross account or tenant boundaries and that logout or permission changes are reflected as expected.

Do not rely only on a cold-cache test. Some vulnerabilities appear only after one identity has populated the cache. Exercise both cache misses and cache hits, then inspect the public response headers to confirm the behavior matches the design.

The principle behind most cache bugs

Shared caching is safe only when two requests that map to the same cache entry are genuinely allowed to receive the same response. Everything else is implementation detail.

When reviewing a CDN, ask two questions repeatedly: "What makes these requests different to the application?" and "Does the cache know about that difference?" If the answer to the second question is no, you have found the place where cache poisoning, deception, or data leakage can begin.