ETag & Conditional Responses
Who is this for? Developers building themes or caching strategies who want HTTP-level cache hits without rebuilding the full page cache layer.
TL;DR: The ETag middleware adds weak ETags to HTML and JSON responses, allowing browsers and HTTP clients to request unchanged pages with a 304 Not Modified response instead of re-downloading the body.
When to use this
Section titled “When to use this”Automatic 304 responses cut bandwidth on repeat visits by skipping response bodies entirely. The ETag middleware works alongside—not instead of—fragment caching and page caching. Use it when you want client-side HTTP cache validation without instrumenting the page cache layer.
How it’s wired
Section titled “How it’s wired”The ETag middleware is registered as a route alias frontend.etag in packages/frontend/src/Providers/FrontendServiceProvider.php (line 280):
Route::aliasMiddleware('frontend.etag', ETagMiddleware::class);It is not automatically applied to all requests—routes must explicitly assign the middleware via route attributes or middleware groups. Apply it to routes where you want conditional response support.
Behavior
Section titled “Behavior”The middleware (packages/frontend/src/Http/Middleware/ETagMiddleware.php) works as follows:
- Computes a weak ETag from the response body using xxHash128 (64-bit truncated to 16 hex characters) and prefixes it with
W/to indicate a weak validator. - Compares to
If-None-Matchheader sent by the client:- If the header value matches the computed ETag exactly, the middleware returns a 304 Not Modified response with an empty body, preserving the original
Cache-ControlandVaryheaders. - If no match or no header, the response passes through with the
ETagheader set.
- If the header value matches the computed ETag exactly, the middleware returns a 304 Not Modified response with an empty body, preserving the original
- Sets
Last-Modifiedheader if not already present on the response, using the current request time in RFC 2822 GMT format. - Skips ETag generation for:
- Non-200 and non-404 status codes.
- Responses without a
Content-Typeheader. - Response content types other than
text/htmlorapplication/json(checked via string position matching).
Enabling on a route
Section titled “Enabling on a route”The ETag middleware is opt-in per route. Apply the frontend.etag alias to any route that should support conditional responses:
Route::get('/products', ProductsController::class)->middleware('frontend.etag');All responses matching the skip conditions above (200/404 status with HTML or JSON content type) will receive ETag headers and conditional response handling.
Gotcha: If a response varies on something the middleware cannot see (e.g. nonce-laden CSP headers, or cache-busting query strings added by JavaScript), it will produce false 304 hits—the client will reuse stale content. Use fragment caching for partials that must vary independently, and ensure cache-busting tokens are baked into the initial response body.
Gotchas
Section titled “Gotchas”- Content-dependent hash: The ETag is computed from the final response body. Any dynamic content that changes per request (CSRF tokens, timestamps, cache-busting nonces) will change the ETag and prevent 304 matches. Use fragment caching for dynamic partials within otherwise-static pages.
- Encoding-sensitive: The hash is computed from the raw response content. If response encoding (gzip vs. identity) or character encoding changes, the ETag will differ and defeat caching.
- HEAD requests: HEAD requests will also receive the
ETagandLast-Modifiedheaders, allowing clients to validate without downloading the body. - Cache-Control interaction: The middleware does not set
Cache-Controlon the outbound response—it preserves any existing value or defaults topubliconly when returning a 304.