Caching keeps the modern web fast, but it also introduces an attack surface where origin rules and edge assumptions drift apart. Web Cache Deception (WCD) takes advantage of that drift: an attacker convinces a cache to store content that should have remained private, then retrieves it before the victim notices. This playbook breaks down how caches make decisions, the primitives that enable WCD, and how red teamers attempt to route around CDNs and proxies.

Reminder: Everything here is for defense, testing, or responsible research. Never target systems you do not own or have permission to assess.

Cache Fundamentals

Layer Examples Typical Scope Trust Assumptions
Browser cache Chrome, Firefox Single user agent Honours Cache-Control headers exactly
Edge cache / CDN CloudFront, Akamai, Cloudflare Shared across clients/regions Derives cache key (host + path + headers). May ignore query strings by default
Reverse proxy Nginx, Varnish, Envoy Per origin POP Usually trusts upstream app path parsing
Application cache In-process, Redis Internal Relies on app to hash the right key

When any layer builds a cache key differently than the origin expects, attackers gain room to trick it.

Anatomy of Web Cache Deception

flowchart LR
    A[Attacker] -->|Craft request| B[CDN / Edge Cache]
    B -->|Forward| C[Origin App]
    C -->|Responds as private| B
    B -->|Stores response| D[(Shared Cache)]
    Attacker -.->|Later fetch| D -.->|Receives private content| A
  1. Attacker requests a victim-only resource (e.g., /account) but dresses the path to look static (/account.css/profile.js).
  2. Origin still authenticates and returns private HTML because it resolves /account after stripping suffixes.
  3. CDN considers the path static (due to suffix) and caches it for everyone.
  4. Attacker replays the cache key and retrieves the stored sensitive response.

Core Deception Techniques

1. Path Confusion

GET /account.php/profile-picture.png HTTP/1.1
Host: target.com
  • Origin behaviour: Normalises everything after /account.php, serves authenticated dashboard.
  • Cache behaviour: Sees .png extension, treats response as static, caches it.
  • Detection tip: Compare X-Cache between /foo and /foo.css/foo; mismatched TTLs indicate exploitable differences.

2. Query String Mismatch

Many CDNs strip query strings unless configured otherwise. Attackers append harmless parameters to differentiate origin routing while keeping the cache key identical.

Original: /profile?format=json           # private, not cached
Deception: /profile;logout?static=1      # origin treats ';logout' as path param, CDN ignores

3. Header Downgrade

  • Some reverse proxies respect X-Original-URL or X-Rewrite-URL while edge caches ignore them.
  • Attackers send GET /static/logo.png with X-Original-URL: /billing, causing the origin to return billing data under a static cache key.

4. Vary Header Abuse

If the origin sets Vary: Cookie but the CDN strips it, private responses may be cached globally. Look for inconsistent Vary coverage in responses that contain personalised data.

5. 404/500 Caching

Misconfigured proxies cache error pages containing stack traces or session data. Trigger with GET /doesnotexist.css, note Cache-Control headers, then fetch again to confirm caching.

CDN and Proxy Bypass Techniques

Technique Goal Notes
Origin pull guessing Hit origin.target.com or backend-target.com to skip CDN entirely Use dig on CNAMEs or certificate transparency logs
Host header pivot Send Host: origin through CDN to force it to proxy raw Some CDNs validate host; others forward blindly
X-Forwarded-Host injection Override virtual host routing inside origin Works when upstream app trusts X-Forwarded-* without allowlist
Double-encoding %2e or %2f sequences bypass CDN path normalization Test /..%2fadmin vs origin behaviour
HTTP verb confusion Some CDNs only cache GET/HEAD; POST might pass through Send POST with _method=GET to see origin response
Cache-busting vs caching Toggle between ?cb=rand (no cache) and path-suffix (force cache) Lets you verify which layer responded

Practical Recon Workflow

# 1. Map cache keys with canned payloads
for suffix in '' '.css' '.js' '.png'; do
  curl -s -D - "https://target.com/account${suffix}" -o /dev/null | grep -Ei 'x-cache|cache-control'
done

# 2. Probe CDN vs origin
hosts=("target.com" "origin.target.com")
for h in "${hosts[@]}"; do
  curl -s -o /dev/null -w "%-20{http_code} %-10{size_download} %{remote_ip}\n" "https://$h/account.css"
done

# 3. Attempt header override
curl -s -D - \
  -H "X-Original-URL: /billing" \
  -H "X-Forwarded-Proto: https" \
  "https://target.com/logo.png"

Exploitation Patterns

  1. Static suffix deception: Append .css, .map, .jpg to dynamic routes.
  2. Path Parameter Smuggling: /dashboard;static=1/style.css (Tomcat/JBoss treat ; as path param, CDN ignores).
  3. Method override: POST /cacheable with X-HTTP-Method-Override: GET.
  4. Response splitting: Poison cache headers using newline injection (rare but high impact).
  5. Edge-side includes (ESI): Abuse <esi:include> fragments if the CDN merges authenticated content into shared templates.

Always verify legally: reproduce only inside authorised staging or bug bounty scopes, disclose responsibly, and provide remediation guidance.

Detection & Telemetry

  • Delta diffing: Capture headers for /private with and without deceptive suffixes.
  • Synthetic monitors: Run scheduled probes that request /profile.css/random. Alert if X-Cache: HIT.
  • Cache-key logging: Enable cf-cache-status, X-Cache-Key, or custom logging on reverse proxies to observe what actually gets cached.
  • WAF correlation: Pair cache hits with authentication logs; if a cache hit lacks a matching login, investigate.

Defensive Playbook

Harden Cache Configuration

# Never cache responses that depend on cookies
types_hash_max_size 4096;
proxy_cache_bypass $http_cookie;
proxy_no_cache $http_cookie;
proxy_cache_key "$scheme$request_method$host$request_uri$http_cookie";
  • Respect Vary headers end-to-end.
  • Default to no cache for authenticated paths; explicitly allow only safe assets.
  • Strip ambiguous suffixes (e.g., enforce .css routing to static directory only).

CDN Rules

  1. Enable cache key normalization: include query strings and critical headers.
  2. Create cache tags: mark /account* as bypass.
  3. Turn on Authenticated Origin Pulls so attackers cannot hit origin directly without presenting a client cert.

Application Guard Rails

  • Serve private content with Cache-Control: private, no-store, max-age=0.
  • Add Content-Disposition: attachment for sensitive document downloads so browsers will not cache them accidentally.
  • Validate request paths strictly before invoking business logic.
  • Ignore untrusted forwarding headers unless they originate from a known proxy IP range.

Monitoring & Response

# Cloudflare example: list cache purge events
curl -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE/cache/purge" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json"

# Akamai: check Property Manager version for cache key changes
akamai property-history --property target.com
  • Purge suspicious cache keys immediately (/login.css*).
  • Rotate session identifiers if you suspect private pages were cached.
  • Instrument SIEM rules for repeated access to suffix-manipulated paths.

Reporting Guidance

When filing a bug bounty or internal report, include:

  1. Affected cache layer (e.g., CloudFront distribution ID).
  2. Exact cache key (method + host + path + query).
  3. Steps to reproduce with timestamps and X-Cache headers.
  4. Evidence that the response contained sensitive data (redact as needed).
  5. Mitigation recommendation: header fix, rule update, or configuration change.

Further Reading


By PlaidNox Security Team
Revised Nov 2025