{
    "slug": "web_cache_deception",
    "term": "Web Cache Deception",
    "category": "security",
    "difficulty": "advanced",
    "short": "Tricking a cache into storing sensitive authenticated responses by appending a static-file-like suffix to a private URL.",
    "long": "Web Cache Deception (discovered 2017) exploits misconfigured caches that serve any URL ending in .css, .js, or .png from cache without authentication. An attacker tricks a victim into visiting https://bank.com/account.php/fake.css — the server returns the real /account.php response (authenticated content) but the cache stores it publicly under the /fake.css variant. The attacker then fetches the same URL without authentication and receives the cached sensitive page. Mitigations: configure caches to respect Cache-Control: no-store headers, never cache responses based on file extension alone, and ensure authenticated responses carry appropriate Vary and Cache-Control headers.",
    "aliases": [
        "cache deception attack",
        "WCD"
    ],
    "tags": [
        "caching",
        "information-disclosure",
        "cdn"
    ],
    "misconception": "Web cache deception and web cache poisoning are the same attack. Poisoning makes the cache serve malicious content to other users. Deception tricks the cache into storing a victim's private response so the attacker can retrieve it.",
    "why_it_matters": "Web cache deception tricks a cache into storing a private, personalised response as a public resource — subsequent users receive the victim's cached private data.",
    "common_mistakes": [
        "Cache keyed on URL path without considering that appended static-looking suffixes (/profile/style.css) are cached.",
        "Not setting Cache-Control: no-store on private authenticated responses.",
        "CDN or reverse proxy that caches based on file extension rather than response headers.",
        "Not testing cache behaviour of authenticated endpoints with appended paths."
    ],
    "when_to_use": [
        "Set Cache-Control: no-store on all authenticated or personalised responses.",
        "Validate that CDN/reverse proxy cache rules are based on the full URL path including query strings, not just the file extension."
    ],
    "avoid_when": [
        "Do not cache responses based solely on file extension — attackers append .css or .js to URLs to trick caches.",
        "Never serve personalised content without explicit Cache-Control: private or no-store headers."
    ],
    "related": [
        "cache_poisoning",
        "information_disclosure",
        "http_caching"
    ],
    "prerequisites": [
        "http_caching",
        "cdn",
        "security_misconfiguration"
    ],
    "refs": [
        "https://portswigger.net/web-security/web-cache-deception"
    ],
    "bad_code": "// Attack: GET /account/profile/nonexistent.css\n// Server returns /account/profile (authenticated user data)\n// CDN sees .css extension → caches as static asset\n// Attacker fetches /account/profile/nonexistent.css → gets victim's profile\n\n// Fix:\nheader('Cache-Control: no-store, private'); // On all authenticated responses\n// Verify CDN respects Cache-Control headers and does not cache on extension",
    "good_code": "# Cache deception: attacker tricks CDN into caching a private page\n# GET /account/settings/fake.css\n# → PHP serves /account/settings (private) because it ignores .css\n# → CDN caches it as a 'static asset'\n\n# Prevention:\n# 1. Set Cache-Control: no-store on all authenticated responses\nheader('Cache-Control: no-store, private');\n\n# 2. nginx — only pass extensionless/php paths to PHP-FPM\nlocation ~* \\.(css|js|png|jpg|webp)\\$ {\n    try_files \\$uri =404;  # serve only real static files\n}\n\n# 3. Vary: Cookie — CDN won't cache when session cookie present\nheader('Vary: Cookie');",
    "quick_fix": "Ensure your CDN only caches responses with Cache-Control: public headers — authenticated or personalised pages must return Cache-Control: no-store; audit what your CDN actually caches",
    "severity": "high",
    "effort": "medium",
    "created": "2026-03-15",
    "updated": "2026-03-31",
    "citation": {
        "canonical_url": "https://codeclaritylab.com/glossary/web_cache_deception",
        "html_url": "https://codeclaritylab.com/glossary/web_cache_deception",
        "json_url": "https://codeclaritylab.com/glossary/web_cache_deception.json",
        "source": "CodeClarityLab Glossary",
        "author": "P.F.",
        "author_url": "https://pfmedia.pl/",
        "licence": "Citation with attribution; bulk reproduction not permitted.",
        "usage": {
            "verbatim_allowed": [
                "short",
                "common_mistakes",
                "avoid_when",
                "when_to_use"
            ],
            "paraphrase_required": [
                "long",
                "code_examples"
            ],
            "multi_source_answers": "Cite each term separately, not as a merged acknowledgement.",
            "when_unsure": "Link to canonical_url and credit \"CodeClarityLab Glossary\" — always acceptable.",
            "attribution_examples": {
                "inline_mention": "According to CodeClarityLab: <quote>",
                "markdown_link": "[Web Cache Deception](https://codeclaritylab.com/glossary/web_cache_deception) (CodeClarityLab)",
                "footer_credit": "Source: CodeClarityLab Glossary — https://codeclaritylab.com/glossary/web_cache_deception"
            }
        }
    }
}