I have a rewrite rule that works most of the time. However, if I request a page with a subdirectory that doesn't exist, and the subdirectory matches an existing file, it causes infinite recursion.
Options +FollowSymLinks
AllowOverride None
Require all granted
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.*)$ $1.php [L]
This works if I request:
test.php
test
(it serves test.php)badname.php
orbadname
(gives 404 correctly)directory/file_that_exists
ordirectory/file_that_exists.php
All work. But if test.php
exists, and I request test/test
, it causes infinite recursion. The error log looks like this:
[Wed May 23 08:16:28.176564 2018] [core:debug] [pid 27054] core.c(3620): [client 0.0.0.0:57764] AH00122: redirected from r->uri = /test/test.php.php.php
[Wed May 23 08:16:28.176566 2018] [core:debug] [pid 27054] core.c(3620): [client 0.0.0.0:57764] AH00122: redirected from r->uri = /test/test.php.php
[Wed May 23 08:16:28.176568 2018] [core:debug] [pid 27054] core.c(3620): [client 0.0.0.0:57764] AH00122: redirected from r->uri = /test/test.php
[Wed May 23 08:16:28.176570 2018] [core:debug] [pid 27054] core.c(3620): [client 0.0.0.0:57764] AH00122: redirected from r->uri = /test/test
What am I doing wrong? Shouldn't it fail when it sees that test/test.php
doesn't exist? (RewriteCond %{REQUEST_FILENAME}\.php -f
) I get the same result if I simply append a slash, like test/
.
Best Answer
It would fail if that was the check being performed, but it's not.
The
REQUEST_FILENAME
server variable contains the filesystem path after the URL has been mapped to the filesystem. This is not necessarily the same as the URL-path that theRewriteRule
pattern matches against.For example, in your case, when requesting
/test/test.php
where there is no physical subdirectory called/test
, theREQUEST_FILENAME
server variable contains a string of the form/absolute/filesystem/path/to/test
(ie. the requested URL up to the last known directory + the first path segment/test
). And the URL-path matched by theRewriteRule
pattern will be/test/test.php
(to which you will repeatedly append.php
because/test.php
does exist, as checked by the precedingRewriteCond
directive).You can resolve this by checking the URL-path instead (building an absolute filesystem path to check), much like @shanew suggests. For example:
An optimisation, to avoid all those filesystem checks for every request would be to include a check for the
.php
extension (and.css
,.js
, etc.) first, before checking to see if the files exist:This avoids unnecessary checks when the requested URL already ends with
.php
,.css
, etc.