From fe2507c4b1f4fc249dcb9e7a009f7894db734672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joachim=20R=C3=BCtter?= Date: Thu, 22 Jan 2026 21:05:18 +0000 Subject: [PATCH] Fix: Prevent 304 responses without client cache headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #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 ✓ --- src/StaticCaching/Middleware/Cache.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/StaticCaching/Middleware/Cache.php b/src/StaticCaching/Middleware/Cache.php index d43b1ec4f71..68265a4bfff 100644 --- a/src/StaticCaching/Middleware/Cache.php +++ b/src/StaticCaching/Middleware/Cache.php @@ -235,9 +235,18 @@ private function outputRefreshResponse($request) private function addEtagToResponse($request, $response) { if (! $response->isRedirect() && $content = $response->getContent()) { - $response - ->setEtag(md5($content)) - ->isNotModified($request); + // Clear any potentially stale cache-related headers that might interfere + $response->headers->remove('ETag'); + $response->headers->remove('Last-Modified'); + + // Set fresh ETag based on current content + $response->setEtag(md5($content)); + + // Only call isNotModified() if request has cache validation headers + // This prevents 304 responses to clients that haven't sent If-None-Match or If-Modified-Since + if ($request->headers->has('If-None-Match') || $request->headers->has('If-Modified-Since')) { + $response->isNotModified($request); + } } return $response;