diff --git a/packages/vitec/Classes/Middleware/JsonCacheHeaders.php b/packages/vitec/Classes/Middleware/JsonCacheHeaders.php new file mode 100644 index 0000000..4b0c5f7 --- /dev/null +++ b/packages/vitec/Classes/Middleware/JsonCacheHeaders.php @@ -0,0 +1,60 @@ +handle($request); + + $path = $request->getUri()->getPath(); + if ( + !\in_array($request->getMethod(), ['GET', 'HEAD'], true) + || $response->getStatusCode() !== 200 + || ($path !== '/_json' && !str_starts_with($path, '/_json/')) + || isset($request->getCookieParams()['fe_typo_user']) + ) { + return $response; + } + + $etag = '"' . md5((string)$response->getBody()) . '"'; + + $ifNoneMatch = trim($request->getHeaderLine('If-None-Match')); + if ($ifNoneMatch !== '' && str_contains($ifNoneMatch, $etag)) { + return (new Response()) + ->withStatus(304) + ->withHeader('ETag', $etag) + ->withHeader('Cache-Control', 'no-cache') + ->withHeader('Expires', '0'); + } + + return $response + ->withHeader('Cache-Control', 'no-cache') + ->withoutHeader('Pragma') + ->withHeader('Expires', '0') + ->withHeader('ETag', $etag); + } +} diff --git a/packages/vitec/Configuration/RequestMiddlewares.php b/packages/vitec/Configuration/RequestMiddlewares.php index 49d6d08..1a70cbb 100755 --- a/packages/vitec/Configuration/RequestMiddlewares.php +++ b/packages/vitec/Configuration/RequestMiddlewares.php @@ -56,6 +56,17 @@ return [ 'typo3/cms-core/normalized-params-attribute', ], ], + // Outer response filter: Cache-Control: no-cache + ETag/304 for + // /_json (tickets 0011-FE / 0055-BE). + 'vitec/json-cache-headers' => [ + 'target' => \Evomedien\Vitec\Middleware\JsonCacheHeaders::class, + 'after' => [ + 'typo3/cms-core/normalized-params-attribute', + ], + 'before' => [ + 'vitec/json-prefix-rewrite', + ], + ], ], 'backend' => [ 'vitec/sync-ssr-flag' => [ diff --git a/public/.htaccess b/public/.htaccess index 20d9563..09bb851 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -439,9 +439,15 @@ Options -MultiViews Header set X-Content-Type-Options "nosniff" -# ETag removal +# ETag removal - except for the /_json API: there TYPO3 (JsonCacheHeaders +# middleware) sends a content ETag so browsers can revalidate with 304 +# (tickets 0011-FE / 0055-BE). - Header unset ETag + SetEnvIf Request_URI "^/_json($|/)" KEEP_ETAG=1 + # After the mod_rewrite internal redirect to index.php the variable + # is renamed REDIRECT_KEEP_ETAG - carry it back over: + SetEnvIf REDIRECT_KEEP_ETAG 1 KEEP_ETAG=1 + Header unset ETag env=!KEEP_ETAG FileETag None