Order of Apache rules for static caching wrong?
On https://statamic.dev/static-caching#apache it shows .htaccess rules for redirecting to the static files. I believe the order these lines is wrong and therefore never hits the actual cache files.
This is in the docs:
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{QUERY_STRING} !live-preview
RewriteRule ^ index.php [L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{QUERY_STRING} live-preview
RewriteRule ^ index.php [L]
RewriteCond %{DOCUMENT_ROOT}/static/%{REQUEST_URI}_%{QUERY_STRING}\.html -s
RewriteCond %{REQUEST_METHOD} GET
RewriteRule .* static/%{REQUEST_URI}_%{QUERY_STRING}\.html [L,T=text/html]
</IfModule>
I kept receiving this message in Laravel logs:
Static cache loaded [..url..] If you are seeing this, your server rewrite rules have not been set up correctly.
I changed it to this, and now the message does not show up anymore:
<IfModule mod_rewrite.c>
RewriteCond %{DOCUMENT_ROOT}/static/%{REQUEST_URI}_%{QUERY_STRING}\.html -s
RewriteCond %{REQUEST_METHOD} GET
RewriteRule .* static/%{REQUEST_URI}_%{QUERY_STRING}\.html [L,T=text/html]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{QUERY_STRING} !live-preview
RewriteRule ^ index.php [L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{QUERY_STRING} live-preview
RewriteRule ^ index.php [L]
</IfModule>
I also added the missing IfModule starting tag. I would happily make a merge request, but I wanted to check if I am not missing something obvious as I don't see any issues raised by other users about this.
Just confirming that I also think this is a mistake in the documentation, that I spent far too long tracking down. I was also seeing the same message in laravel.log and after correcting the order the messages went away.
As part of my debugging I also ran the order through Claude which returned:
Why Static Cache is Last The key insight is understanding when these rules actually execute:
- If a real file/directory exists (like /css/style.css or /images/logo.png), none of these rules fire at all - Apache serves the file directly
- If no real file exists, the first two rules will match and send the request to index.php with the [L] flag, stopping further rule processing
This means Rule 3 will never execute with this configuration!
This appears to be either:
- A documentation error showing rules in the wrong order
- An example of what NOT to do
- Missing context (perhaps there are other rules not shown)
Correct Order Should Be
# Static cache first RewriteCond %{DOCUMENT_ROOT}/static/%{REQUEST_URI}_%{QUERY_STRING}\.html -s RewriteCond %{REQUEST_METHOD} GET RewriteRule .* static/%{REQUEST_URI}_%{QUERY_STRING}\.html [L,T=text/html] # Then fallback to index.php for non-existent files RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{QUERY_STRING} !live-preview RewriteRule ^ index.php [L] # Live preview handling RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{QUERY_STRING} live-preview RewriteRule ^ index.php [L]This way, Apache first checks for cached static files, and only falls back to the PHP application if no cache exists.