Aapche2 htaccess RewriteCond with ErrorDocument 404

Viewed 100

Aapche2 htaccess RewriteCond

i found some wondering Condition that works , but i dont know why :)

Files

/error.php
/donate.php
/test/index.php

in a htaccess file i use

ErrorDocument 404 /error.php
RewriteEngine On
# WHY THIS LINE NEEDED TO GET IT WORKS 
RewriteCond %{REQUEST_FILENAME} ^$
#
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.*) $1.php [L]

this .

http://localhost/donate calls internal http://localhost/donate.php

while

http://localhost/donate1 calls internal http://localhost/error.php 

and

http://localhost/test/ call internal http://localhost/test/index.php

so far so good

but when i comment it out

#RewriteCond %{REQUEST_FILENAME} ^$

then i get internal server error while call /donate1 and not the /error.php

can someone explain the steps , why this happens ?

1 Answers
#RewriteCond %{REQUEST_FILENAME} ^$

Because your directives are not actually doing what you think they are doing. In fact, with that "hacky" condition uncommented they are not doing anything at all, except to prevent the 500 Internal Server Error (which is due to an internal rewrite loop because the rule is strictly incorrect).

That condition checks if the REQUEST_FILENAME server variable is empty. It is never empty, so always fails, so the RewriteRule directive that follows is never triggered.

You could remove your mod_rewrite directives entirely and you'll get the same results.

http://localhost/donate calls internal http://localhost/donate.php

It's most probably MultiViews (mod_negotiation) that is rewriting /donate to /donate.php. Not the directives you posted (which, as I mentioned, don't actually do anything).

http://localhost/test/ call internal http://localhost/test/index.php

This is caused by mod_dir (DirectoryIndex). Again, nothing to do with the directives you posted.

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.*) $1.php [L]

then i get internal server error while call /donate1 and not the /error.php

Because when you request /donate1 the above directives trigger an internal rewrite loop (which results in a 500 Internal Server Error response). /donate1 to /donate1.php to /donate1.php.php to /donate1.php.php.php etc. (see below).

MultiViews does not apply here because there is no file that /donate1 can perceivably map to, eg. /donate1.php or /donate1.html or some other recognised resource, with a different file extension, that returns a text/html mime-type.

When you request /donate1 the following happens.

  1. /donate1 does not map to a directory (1st condition) or a file (2nd condition) so is internally rewritten by this rule to donate1.php. Which is incorrect (but that is what this rule does).
  2. The L flag then causes the current round of processing to stop and the rewrite engine starts over, passing the rewritten URL, ie. donate1.php back into the mix.
  3. /donate1.php does not map to a directory or file so is rewritten to donate1.php.php.
  4. The rewrite engine starts over...
  5. /donate1.php.php does not map to a directory or file so is rewritten to donate1.php.php.php.
  6. The rewrite engine starts over...
  7. etc.

This repeats until 10 (default) internal rewrites are reached and the server "breaks" with a 500 error response. The server error log would contain the details of this error, for example:

AH00124: Request exceeded the limit of 10 internal redirects due to probable configuration error. Use 'LimitInternalRecursion' to increase the limit if necessary. Use 'LogLevel debug' to get a backtrace.

(Although very rarely would you ever need to change this internal redirect limit - it nearly always indicates an error in your script.)

Solution

You either remove your mod_rewrite directives entirely and just let MultiViews do its thing, OR you disable MultiViews and "correct" your mod_rewrite directives.

For example:

Options -MultiViews

ErrorDocument 404 /error.php

RewriteEngine On

# Rewrite extensionless URLs to ".php" if they exist
RewriteCond %{DOCUMENT_ROOT}/$1.php -f
RewriteRule (.+) $1.php [L]

An optimisation... if your URLs (that map to .php files) don't contain dots then you could simply exclude URLs that contain dots so you don't unnecessarily test requests for your static resources (eg. image.jpg, styles.css, etc.) that already include a file extension (which naturally contain a dot before the file extension):

RewriteRule ([^.]+) $1.php [L]

Reference:

Related