I am looking to exclude a rule from being called if the page does not exist. In my existing htaccess, this rule works as intended when the user accesses a page that does not exist from the root. Oddly enough, it does not work if the user tries to access a subdirectory of a valid page that does not exist.
The current behavior of my htaccess:
example.com/valid-page/ -> example.com/valid-page/ (good)
example.com/valid-page -> example.com/valid-page/ (good)
example.com/valid-page/valid-page -> example.com/valid-page/valid-page/ (good)
example.com/does-not-exist -> example.com/does-not-exist (good)
example.com/valid-page/does-not-exist -> example.com/valid-page/does-not-exist/ (boo!)
With the bad example above, the desired outcome should be not to included the forward slash:
example.com/valid-page/does-not-exist -> example.com/valid-page/does-not-exist
Here is the .htaccess:
# Redirects for errors
ErrorDocument 404 /error.php
ErrorDocument 403 /error.php
# Hides directory file listings
Options -Indexes
# Turn mod_rewrite on
RewriteEngine On
RewriteBase /
# Force slash at end of URL
RewriteCond %{REQUEST_URI} / [^\.] $
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(. [^/])$ %{REQUEST_URI}/ [R=301,L]
# To externally redirect /dir/foo.php to /dir/foo
RewriteCond %{THE_REQUEST} ^[A-Z]{3,}\s([^.] )\.php [NC]
RewriteRule ^ %1 [R,L]
# To internally forward /dir/foo to /dir/foo.php
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteRule ^([^.] )/$ $1.php [NC,L]
CodePudding user response:
# Force slash at end of URL
RewriteCond %{REQUEST_URI} / [^\.] $
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(. [^/])$ %{REQUEST_URI}/ [R=301,L]
You need to change the 2nd condition that checks against REQUEST_FILENAME
, because when you request /valid-page/does-not-exist
, REQUEST_FILENAME
is .../valid-page
, not .../valid-page/does-not-exist
as you are expecting.
Instead of REQUEST_FILENAME
, use REQUEST_URI
(or the $1
backreference) and construct the absolute filename instead. For example:
# Force slash at end of URL
RewriteCond %{REQUEST_URI} / [^\.] $
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.php -f
RewriteRule ^(. [^/])$ %{REQUEST_URI}/ [R=301,L]
You will need to clear your browser cache before testing since the erroneous 301 (permanent) redirect will have been cached by the browser. Test with 302 (temporary) redirects to avoid caching issues.
Now, when you request /valid-page/does-not-exist
you are checking whether .../valid-page/does-not-exist.php
exists as a file, not .../valid-page.php
as before.
See my answer to the following question on ServerFault which goes into more detail about what REQUEST_FILENAME
is actually set to. https://serverfault.com/questions/989333/using-apache-rewrite-rules-in-htaccess-to-remove-html-causing-a-500-error