docs icon indicating copy to clipboard operation
docs copied to clipboard

Order of Apache rules for static caching wrong?

Open marnickmenting opened this issue 1 year ago • 1 comments

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.

marnickmenting avatar Jan 14 '25 09:01 marnickmenting

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:

  1. 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
  2. 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.

benlilley avatar Jul 01 '25 05:07 benlilley