I am using MAMP, with a virtual-host based setup creating a '.dev' tld for working on. My site's .htaccess uses mod_rewrite like so:
RewriteEngine on
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.*)$ $1.php [L]
RewriteRule ^(butchers|news|recipes)/([a-zA-Z0-9-/]+)$ /$1.php?s=$2 [L,QSA]
My website features a directory of butcher's shops. The list view is displayed by butchers.php
accessible at butchers
. The detail view is displayed by butchers.php?s=some-butcher-name
accessible at butchers/some-butcher-name
. The same mechanism works for the News and Recipes sections, as you can see.
Ok here's the thing: this used to work. It worked fine, and then I rebooted my system some time after upgrading MAMP and now it gives me an Internal server error. I assume I've gone from Apache 1.x to 2.x; I had to redo my vhost config after upgrading but everything else is vanilla (same as last time) except that I have disabled MultiViews
. The mod_rewrite log looks like this (first few columns truncated)
[perdir /Users/myusername/Sites/dev/sitename/] add path info postfix: /Users/myusername/Sites/dev/sitename/butchers -> /Users/myusername/Sites/dev/sitename/butchers/example-butcher
[perdir /Users/myusername/Sites/dev/sitename/] strip per-dir prefix: /Users/myusername/Sites/dev/sitename/butchers/example-butcher -> butchers/example-butcher
[perdir /Users/myusername/Sites/dev/sitename/] applying pattern '^(.*)$' to uri 'butchers/example-butcher'
[perdir /Users/myusername/Sites/dev/sitename/] RewriteCond: input='/Users/myusername/Sites/dev/sitename/butchers' pattern='!-d' => matched
[perdir /Users/myusername/Sites/dev/sitename/] RewriteCond: input='/Users/myusername/Sites/dev/sitename/butchers' pattern='!-f' => matched
[perdir /Users/myusername/Sites/dev/sitename/] RewriteCond: input='/Users/myusername/Sites/dev/sitename/butchers.php' pattern='-f' => matched
[perdir /Users/myusername/Sites/dev/sitename/] rewrite 'butchers/example-butcher' -> 'butchers/example-butcher.php'
[perdir /Users/myusername/Sites/dev/sitename/] add per-dir prefix: butchers/example-butcher.php -> /Users/myusername/Sites/dev/sitename/butchers/example-butcher.php
[perdir /Users/myusername/Sites/dev/sitename/] trying to replace prefix /Users/myusername/Sites/dev/sitename/ with /
strip matching prefix: /Users/myusername/Sites/dev/sitename/butchers/example-butcher.php -> butchers/example-butcher.php
add subst prefix: butchers/example-butcher.php -> /butchers/example-butcher.php
[perdir /Users/myusername/Sites/dev/sitename/] internal redirect with /butchers/example-butcher.php [INTERNAL REDIRECT]
Here's my understanding of what's happening:
butchers/example-butcher
butchers
, not butchers/example-butcher
butchers.php
is realbutchers/example-butcher
butchers/example-butcher.php
Then process starts again with the same problem - that the RewriteCond tests this weird, incomplete version of the path, but operates on the correct path. This happens ten times, accumulating more and more .php
's, before apache hits the limit and issues a 500.
butchers/example-butcher
butchers.php
a real file? yes!.php
butchers/example-butcher.php
butchers.php
a real file? yes!.php
butchers/example-butcher.php.php
So, I can see why there is a loop, and why it wants to keep adding .php
to itself, I just don't understand why REQUEST_FILENAME only ever gives the first level of the path. I can't find any documentation that says that this is prescribed behaviour.
Any ideas? I've already burned a whole workday on this :/
Thanks!
The reason for this is because the %{REQUEST_FILENAME}
variable isn't simply the requested URI mapped to a single file/directory. When you have a request like:
/foo/bar/something
And you have either the files:
/foo.php
/foo/bar.php
/foo/bar/something.php
the condition:
RewriteCond %{REQUEST_FILENAME}.php -f
will be true because mod_rewrite also accounts for PATH_INFO. If you have one of those php files, then mod_rewrite will attempt to be clever and figure out if a requested URI is actually within the requested URI path and add the .php
to that path node instead of at the very end. To strictly check if the request + .php
exists, you need to do:
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.php -f