I've been messing around with what I thought would be a very simple htaccess file (given that I have a decent knowledge of regex) for a long time, and I finally got it working by doing something I expected to be pointless. The recurring problem was that the [L] flag was not stopping the rewriting process. ie, one rule with the last flag would match a url and rewrite it, and then the next rule would match the url that request was rewritten to and rewrite it again.
The only solution I've found is to add in another rule which matches the url the first rule rewrites to, does not modify it, and also has the [L] flag. Here is the complete file - the "INTERNAL REDIRECTS" section should be the only one you need to look at, but since I'm no apache guru I'm not sure that the rest of the code isn't having effects I don't know about.
# ERROR DOCUMENTS
ErrorDocument 403 /error/403.html
ErrorDocument 404 /error/404.html
ErrorDocument 500 /error/500.html
RewriteEngine on
# ACCESS CONTROLS
RewriteRule \.ini$ - [F]
RewriteRule (/|^)\.ht(access|passwd|group)$ - [F]
# EXTERNAL REDIRECTS
RewriteCond %{HTTP_HOST} !^(example\.com)?$
RewriteRule ^(.*)$ http://example.com/$1 [R=301,L]
# INTERNAL REDIRECTS
RewriteCond %{QUERY_STRING} ^$
RewriteRule ^(s/|(index\.php)?$) - [L]
RewriteRule ^.+?/.+ details.php [L]
RewriteRule details.php - [L]
RewriteRule ^[^/]+$ list.php [L]
The intent (and effect) is to pass through requests for the index page or anything in the /s directory, rewrite everything 2 levels deep (example.com/category/item) to details.php, and rewrite everything 1 level deep (example.com/category) or 0 but with a query string (example.com/?price=10-30) to list.php.
The line matching /s and the index effectively stops execution yet the next line does not (the file fails if the line after that is removed). Why? Does [L] only work for rules that don't do anything (rewrite to "-")?