Labs Covered
This write-up focuses on the following PRACTITIONER-level labs from the PortSwigger Web Security Academy related to Web Cache Deception:
2 Exploiting path delimiters for web cache deception
This lab demonstrates how attackers can abuse path delimiter variations to trick caches into storing sensitive content.
3 Exploiting origin server normalization for web cache deception
This lab shows how mismatches in URL normalization between cache and origin servers can be exploited.
4 Exploiting cache server normalization for web cache deception
This lab covers techniques to exploit cache server normalization issues to achieve web cache deception.
LAB 2 - Exploiting path delimiters for web cache deception
Lab Description
You’re right — thanks for pointing that out! Here’s the updated professional overview section with delimiter discrepancies, delimiter decoding discrepancies, and framework behavior all incorporated:
Overview: Delimiter Discrepancies and Web Cache Deception
Delimiter discrepancies arise when the origin server and the cache server interpret special characters in URL paths differently. These mismatches can allow an attacker to append fake static extensions (e.g., .css, .js, .ico) to dynamic endpoints and trick the cache into storing private, user-specific content.
Many web frameworks interpret delimiters in unique ways:
;is used for matrix parameters in Java Spring..denotes response format in Ruby on Rails.%00(null byte) is treated as a terminator in OpenLiteSpeed.
In contrast, most caching layers (CDNs, proxies) treat these characters literally as part of the file path. This allows attackers to:
- Forge a URL like
/account;extra.cssor/user.json, which the origin server ignores or truncates. - The origin server returns dynamic data (e.g., a user’s profile).
- The cache server, seeing the fake static extension, caches the response as if it were a static resource.
For example:
/profile;foo.css → origin returns profile info (ignores `;foo.css`), cache stores it under `.css`
Delimiter Decoding Discrepancies
A second layer of this vulnerability arises when encoded delimiters like %23 (URL-encoded #) or %3F (?) are decoded by the origin but not by the cache, or vice versa. This can cause further inconsistency in how the path is parsed.
Example:
/profile%23wcd.css
→ Cache sees: /profile%23wcd.css (matches `.css` rule)
→ Origin decodes %23 to `#`, interprets path as `/profile`
Similarly:
/myaccount%3fwcd.css
→ Cache applies `.css` rule to encoded path
→ Then decodes `%3F` → `?` before forwarding
→ Origin sees `/myaccount?wcd.css` and ignores query
These decoding mismatches are often framework- and cache-specific. Some caches decode before matching cache rules, while others decode after. This behavior enables attackers to inject misleading extensions without detection.
Exploiting These Behaviors
To identify and exploit delimiter-based vulnerabilities:
- Use Burp Suite and Intruder to test common delimiters (
;,.,!,%00, etc.) - Test both raw and encoded forms of delimiters (
;vs%3B,?vs%3F) - Observe differences in server behavior, response headers (e.g.,
X-Cache), and response content
If the cache stores a dynamic response under a crafted path — and the origin server ignores the added part — you’ve successfully triggered Web Cache Deception.
Let me know if you’d like this incorporated into your .md file or published on GitHub with your previous write-ups.
Solution
Before starting, configure FoxyProxy to intercept requests through Burp Suite. Ensure that ‘Intercept’ is turned off in Burp Suite while FoxyProxy is active, so that all requests are logged in the HTTP history. Then, log in to the application using the credentials wiener:peter.
Please note that the response will include your API key.
Go to Burpuite Proxy > HTTP history, right-click the GET /my-account request and select Send to Repeater.
Go to the Repeater tab and modify the path by adding an arbitrary segment. For example, change the path to /my-account/abc. Send the request and observe the 404 Not Found response without any evidence of caching. This indicates that the origin server does not abstract the path to /my-account.
Next, remove the arbitrary segment and append an arbitrary string to the original path. For instance, update the path to /my-accountabc. Send the request and note the 404 Not Found response with no signs of caching. This response will serve as a reference for identifying characters that are not used as delimiters.
Right-click the request and select “Send to Intruder” In the Intruder tab, ensure the Sniper attack type is selected and set a payload position after /my-account as follows: /my-account§§abc.
Paste the delimiter payload list from PortSwigger’s official lab into Burp Intruder’s payload positions for systematic testing.
Under Payload encoding, deselect URL-encode these characters.
After the attack completes, review the results. You should find that the ; and ? characters return a 200 response with your API key, while other characters return a 404 Not Found response. This suggests that the origin server uses ; and ? as path delimiters.
Go back to the Repeater tab containing the /my-accountabc request. Add a ? character after /my-account and append a static file extension to the path, such as /my-account?abc.js. Send the request and observe that the response does not indicate caching. This suggests that the cache also uses ? as a path delimiter.
In Burp’s browser, click “Go to exploit server.” In the Body section, craft an exploit to redirect the victim user, Carlos, to the malicious URL you crafted earlier. Make sure to update the arbitrary string to create a unique cache key, so Carlos’s account details are cached instead of the previously cached response:
<script>document.location="https://YOUR-LAB-ID.web-security-academy.net/my-account;hanzala.js"</script>
Store and click “Deliver exploit to victim.” When Carlos views the exploit, the response they receive will be stored in the cache.
Return to the Repeater tab, resend the request, and retrieve the response, which should now include Carlos’s API key. Copy the key.
Submit it to complete the lab.
LAB 3 - Exploiting origin server normalization for web cache deception
Lab Description
Overview: Static Directory Rules and Path Normalization Discrepancies
Static directory cache rules are common configurations where web caches store resources under path prefixes like /static, /assets, or /scripts. These directories usually serve static content such as images, scripts, or stylesheets. However, if there’s a discrepancy between how the origin server and the cache server normalize URL paths, this setup can become vulnerable to Web Cache Deception.
The root cause lies in path normalization, where URLs are standardized by decoding encoded characters and resolving dot-segments (e.g., ../). If the origin server and cache server normalize paths differently, an attacker can construct a path traversal payload that bypasses cache rules and exposes sensitive content.
Example:
/static/..%2fprofile
→ Cache sees: /static/..%2fprofile (matches static directory rule)
→ Origin resolves to: /profile (returns sensitive user data)
→ Cached under static path
By placing traversal sequences like ..%2f inside a “safe” static path, an attacker can:
- Bypass access controls enforced by static cache directories.
- Trick the cache into storing private responses under a path that looks harmless.
- Retrieve the cached response by simply visiting the crafted static-path URL.
Identifying this vulnerability requires careful testing of how both the origin server and cache behave during normalization. Using tools like Burp Suite, HTTP history analysis, and MIME-type filtering, you can detect inconsistencies in dot-segment resolution and slash decoding that enable exploitation.
Solution
Before starting, configure FoxyProxy to intercept requests through Burp Suite. Ensure that ‘Intercept’ is turned off in Burp Suite while FoxyProxy is active, so that all requests are logged in the HTTP history. Then, log in to the application using the credentials wiener:peter.
Please note that the response will include your API key.
In the Proxy > HTTP History section, observe that the paths for static resources consistently begin with the directory prefix /resources. Additionally, note that responses to requests containing the /resources prefix exhibit signs of caching. Right-click on a request with the /resources prefix and select “Send to Repeater.”
In Repeater, append an encoded dot-segment to the /resources path prefix, for example, /resources/..%2fRESOURCES. Send the request and observe that the 404 response includes the X-Cache: miss header.
Resend the request and note that the value of the X-Cache header changes to hit. This may suggest that the cache does not decode or resolve the dot-segment, instead applying a cache rule based on the /resources prefix
Change the request to /aaa/..%2fmy-account. Attempt to construct an exploit by using the path /resources/..%2fmy-account and send the request. Note that this results in a 200 response containing your API key and the X-Cache: miss header.
Resend the request to observe that the X-Cache header value updates to hit.
In Burp Suite’s browser, click “Go to exploit server.” In the Body section, craft an exploit designed to navigate the victim user, Carlos, to a malicious URL. Ensure to include an arbitrary parameter as a cache buster so the victim does not receive the previously cached response:
<script>document.location="https://YOUR-LAB-ID.web-security-academy.net/resources/..%2fmy-account"</script>
Click “Deliver exploit to victim.” When Carlos views the exploit, the response is stored in the cache.
Visit the URL you delivered to Carlos in your exploit. If you have used Burp Suite for navigation, ensure to check the response to confirm that it includes the API key for user Carlos.
https://YOUR-LAB-ID.web-security-academy.net/resources/..%2fmy-account
Observe that the response includes the API key for user Carlos. Copy this key, click “Submit solution,” and then submit the API key to complete the lab.
LAB 4 - Exploiting cache server normalization for web cache deception
Lab Description
Overview: Cache-Side Normalization Discrepancies
When the cache server normalizes paths differently from the origin server, particularly by resolving encoded dot-segments, it can lead to exploitable discrepancies. Specifically, if the cache decodes sequences like %2f%2e%2e%2f (i.e., /../) but the origin server treats them as literal text, an attacker can manipulate request paths to trick the cache into storing sensitive content under a static directory.
This technique leverages two components:
- Cache normalization: The cache resolves encoded traversal sequences and maps the request to a static directory (e.g.,
/static). - Origin delimiter truncation: The origin server interprets a delimiter (e.g.,
;,?,%00) and truncates the path, returning sensitive dynamic content.
Exploit Flow Example:
/profile;%2f%2e%2e%2fstatic
→ Cache resolves to: /static (stores response)
→ Origin server truncates at `;` and processes /profile (returns dynamic content)
In this setup:
- The cache sees a request to a static resource and stores the response.
- The origin server, due to delimiter usage, returns user-specific or sensitive data.
- The attacker later accesses the cached version of the crafted path and retrieves the exposed data.
Understanding this discrepancy allows an attacker to craft cross-behavior payloads that combine encoded path traversal with delimiter manipulation, resulting in powerful Web Cache Deception exploits even when traditional traversal techniques fail.
Solution
Before starting, configure FoxyProxy to intercept requests through Burp Suite. Ensure that ‘Intercept’ is turned off in Burp Suite while FoxyProxy is active, so that all requests are logged in the HTTP history. Then, log in to the application using the credentials wiener:peter.
Please note that the response will include your API key.
In Proxy > HTTP history, notice that static resources share the URL path directory prefix /resources. Notice that responses to requests with the /resources prefix show evidence of caching.
Right-click a request with the prefix /resources and select Send to Repeater.
In Repeater, add an encoded dot-segment and arbitrary directory before the /resources prefix. For example, /aaa/..%2fresources/YOUR-RESOURCE.
Send the request. Notice that the 404 response contains the X-Cache: miss header.
Resend the request. Notice that the value of the X-Cache header updates to hit. This may indicate that the cache decodes and resolves the dot-segment and has a cache rule based on the /resources prefix. To confirm this, you’ll need to conduct further testing. It’s still possible that the response is being cached due to a different cache rule.
Add an encoded dot-segment after the /resources path prefix as follows: /resources/.%2fYOUR-RESOURCE.
Send the request. Notice that the 404 response no longer contains evidence of caching. This indicates that the cache decodes and resolves the dot-segment and has a cache rule based on the /resources prefix.
Go to the Repeater tab that contains the /aaa/..%2fmy-account request. Use the ? delimiter to attempt to construct an exploit as follows: /my-account%2f%2e%2e%2fresources
Send the request. Notice that this receives a 200 response with your API key, but doesn’t contain evidence of caching.
Repeat this test using the %23 and %3f characters instead of ?. Notice that when you use the %23 character this receives a 200 response with your API key and the X-Cache: miss header.
Resend and notice that this updates to X-Cache: hit. You can use this delimiter for an exploit.
In Burp’s browser, click Go to exploit server.
In the Body section, craft an exploit that navigates the victim user carlos to a malicious URL. Make sure to add an arbitrary parameter as a cache buster:
<script>document.location="https://YOUR-LAB-ID.web-security-academy.net/my-account%23%2f%2e%2e%2fresources?hanzala"</script>
Click Deliver exploit to victim.
Go to the URL that you delivered to carlos in your exploit:
https://YOUR-LAB-ID.web-security-academy.net/my-account%23%2f%2e%2e%2fresources?hanp
Notice that the response includes the API key for the user carlos. Copy this.
Click Submit solution, then submit the API key for carlos to solve the lab.