# Cache key normalization mismatch

Edge and origin disagree on path/URL normalization or error caching, so a bad response lands under a valid key

_Area: Web infrastructure and server-side requests. Seen in 3 disclosed reports; 2 confirmed bounties: $500 and $3,800; latest 2025-06-04. Updated 2026-10-24._

## How it works
A cache stores responses under a key it computes from the request, and later serves that stored response to anyone whose request maps to the same key. This is only safe if the cache and the origin agree on what counts as "the same request." When they normalize differently — collapsing backslashes to slashes, ignoring a header, or caching an error that the origin produced only for a malformed variant — a response generated for one request gets stored under the key of a different, legitimate request. The broken assumption is that cache key equality implies origin response equality.

## Where it shows up
- CDN/edge in front of an origin that handles path separators, encoding, or case differently [E1](https://bugbountylibrary.com/r/h1%3A1695604.md)
- Endpoints returning session-dependent private data with ETag/Last-Modified but no `Cache-Control: no-store` [E2](https://bugbountylibrary.com/r/blog%3Aenumerated-wordpress-com-2019-12-24-sop-bypass-via-browser-cache.md)
- Backends that error on specific header combinations while the CDN still caches the error [E3](https://bugbountylibrary.com/r/blog%3Alandh-tech-blog-20240603-npm-cache-poisoning.md)
- Any 404/5xx response being cacheable under a key that also serves real content

## How to review
The invariant: for every request the cache will treat as key-equal, the origin must return an equivalent response, and error responses must never be stored under keys that serve valid content. Check:
- Does the edge normalize paths (backslash, encoding, dot-segments) the same way the origin does? [E1](https://bugbountylibrary.com/r/h1%3A1695604.md)
- Are error responses (404/5xx) cacheable at all, and under which keys? [E1](https://bugbountylibrary.com/r/h1%3A1695604.md)[E3](https://bugbountylibrary.com/r/blog%3Alandh-tech-blog-20240603-npm-cache-poisoning.md)
- Do authenticated responses carry `Cache-Control: no-store` and `Vary` on the right inputs? [E2](https://bugbountylibrary.com/r/blog%3Aenumerated-wordpress-com-2019-12-24-sop-bypass-via-browser-cache.md)
- Can a malformed request trigger an origin error that the CDN then stores for clean requests? [E3](https://bugbountylibrary.com/r/blog%3Alandh-tech-blog-20240603-npm-cache-poisoning.md)

## Variants seen
- Path separator normalization mismatch: backslash treated as slash at edge, 404 at origin [E1](https://bugbountylibrary.com/r/h1%3A1695604.md)
- Browser-cache variant: private authenticated response cached and read cross-origin via force-cache [E2](https://bugbountylibrary.com/r/blog%3Aenumerated-wordpress-com-2019-12-24-sop-bypass-via-browser-cache.md)
- Header-triggered backend error cached as 404 for a valid artifact [E3](https://bugbountylibrary.com/r/blog%3Alandh-tech-blog-20240603-npm-cache-poisoning.md)

## Impact and severity
Led to denial of service against shared assets and package installs [E1](https://bugbountylibrary.com/r/h1%3A1695604.md)[E3](https://bugbountylibrary.com/r/blog%3Alandh-tech-blog-20240603-npm-cache-poisoning.md) and disclosure of private user data (email, billing, keys) [E2](https://bugbountylibrary.com/r/blog%3Aenumerated-wordpress-com-2019-12-24-sop-bypass-via-browser-cache.md). Severity rises with blast radius: shared CDN keys affect all users, and private-data caching exposes per-victim secrets. Missing `no-store` and cacheable errors made it worse.

## Evidence
- E1: https://bugbountylibrary.com/r/h1%3A1695604.md
- E2: https://bugbountylibrary.com/r/blog%3Aenumerated-wordpress-com-2019-12-24-sop-bypass-via-browser-cache.md
- E3: https://bugbountylibrary.com/r/blog%3Alandh-tech-blog-20240603-npm-cache-poisoning.md
