Forcing 304 Not Modified
The If-None-Match header makes a request conditional, creating a 304 vs 404 existence oracle.
Mechanism: The If-None-Match header makes a request conditional. Per RFC 9110 §13.1.2, when a GET request includes If-None-Match: *, the condition evaluates to false if any current representation exists — meaning the server responds 304 Not Modified. If the resource doesn't exist, no representation exists, and the server returns 404.
Isolated Variable: Only the If-None-Match header is added with a wildcard * value. Everything else matches the baseline request.
Oracle Signal: 304 (exists) vs 404 (does not exist).
GET — Existing Resource
GET /api/users/1001 HTTP/1.1
Host: target.com
If-None-Match: *
HTTP/1.1 304 Not Modified
ETag: "a1b2c3d4"GET — Non-Existing Resource
GET /api/users/9999 HTTP/1.1
Host: target.com
If-None-Match: *
HTTP/1.1 404 Not Found
Content-Type: application/json
{"error": "Not Found"}💡 Note on
If-None-Matchwith specific ETags: You can also use a fabricated ETag value (e.g.,If-None-Match: "bogus") instead of the wildcard. Against an existing resource, the ETags won't match, so the server returns200with the full representation. Against a non-existing resource, you get404. The wildcard*variant is cleaner because the304is a distinct status code that's unambiguous.
Mitigation: This oracle is inherent to HTTP conditional request semantics and is difficult to collapse without breaking caching. The most practical defense is to require authentication before evaluating conditional headers.
Mechanism: The If-None-Match header makes a request conditional. Per RFC 9110 §13.1.2, when a GET request includes If-None-Match: *, the condition evaluates to false if any current representation exists — meaning the server responds 304 Not Modified. If the resource doesn't exist, no representation exists, and the server returns 404.
Isolated Variable: Only the If-None-Match header is added with a wildcard * value. Everything else matches the baseline request.
Oracle Signal: 304 (exists) vs 404 (does not exist).
GET — Existing Resource
GET /api/users/1001 HTTP/1.1
Host: target.com
If-None-Match: *
HTTP/1.1 304 Not Modified
ETag: "a1b2c3d4"GET — Non-Existing Resource
GET /api/users/9999 HTTP/1.1
Host: target.com
If-None-Match: *
HTTP/1.1 404 Not Found
Content-Type: application/json
{"error": "Not Found"}💡 Note on
If-None-Matchwith specific ETags: You can also use a fabricated ETag value (e.g.,If-None-Match: "bogus") instead of the wildcard. Against an existing resource, the ETags won't match, so the server returns200with the full representation. Against a non-existing resource, you get404. The wildcard*variant is cleaner because the304is a distinct status code that's unambiguous.
Mitigation: This oracle is inherent to HTTP conditional request semantics and is difficult to collapse without breaking caching. The most practical defense is to require authentication before evaluating conditional headers.