Skip to content

Conversation

@mynetx
Copy link
Contributor

@mynetx mynetx commented Jan 22, 2026

Fixes #13652

Problem

The static caching middleware was unconditionally calling isNotModified() on every cached response, which could incorrectly return HTTP 304 to clients that didn't send cache validation headers (If-None-Match or If-Modified-Since). This caused browsers like Safari to download 0-byte files instead of rendering pages.

Root Cause

  1. ApplicationCacher may cache responses with ETag/Last-Modified headers from previous requests
  2. When serving cached responses, these stale headers interfere with fresh ETag generation
  3. isNotModified() evaluates these cached headers and incorrectly returns true even without client validation headers

Solution

This PR updates src/StaticCaching/Middleware/Cache.php to:

  1. Clear stale headers: Remove any existing ETag/Last-Modified headers before setting a fresh ETag
  2. Conditional check: Only call isNotModified() when the request contains cache validation headers (If-None-Match or If-Modified-Since)

This preserves ETag functionality for legitimate conditional requests while preventing inappropriate 304 responses.

Testing

  • ✅ Request without cache headers → HTTP 200 OK (bug fixed)
  • ✅ Request with If-None-Match → HTTP 304 Not Modified (ETag functionality preserved)
  • ✅ No breaking changes to existing functionality

Related

This is related to the ETag support feature introduced in v5.68 (PR #11441), which has had one previous bug fix in v5.70 (#13425).

Fixes statamic#13652

The static caching middleware was unconditionally calling isNotModified()
on every cached response, which could incorrectly return HTTP 304 to
clients that didn't send cache validation headers (If-None-Match or
If-Modified-Since). This caused browsers like Safari to download 0-byte
files instead of rendering pages.

Root cause:
- ApplicationCacher may cache responses with ETag/Last-Modified headers
  from previous requests
- When serving cached responses, these stale headers interfere with
  fresh ETag generation
- isNotModified() evaluates these cached headers and incorrectly
  returns true even without client validation headers

Solution:
1. Clear any stale ETag/Last-Modified headers before setting fresh ETag
2. Only call isNotModified() when request contains cache validation
   headers (If-None-Match or If-Modified-Since)

This preserves ETag functionality for legitimate conditional requests
while preventing inappropriate 304 responses.

Tested:
- Request without cache headers → 200 OK ✓
- Request with If-None-Match → 304 Not Modified ✓
@mynetx mynetx changed the title Fix: Prevent 304 responses without client cache headers [5.x] Fix: Prevent 304 responses without client cache headers Jan 22, 2026
@jasonvarga jasonvarga merged commit 7b7bddf into statamic:5.x Jan 22, 2026
27 of 28 checks passed
@jasonvarga
Copy link
Member

Thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Static caching returns 304 without client cache headers, causing 0-byte downloads

2 participants