“Spoofing” a 404 not found error with .htaccess

2020-04-14 07:53发布

问题:

I have .htaccess currently set up to rewrite anything without a period or slash to the equivalent with a .php extension (so "foo" internally pulls up "foo.php") via the following:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !(.*)\.php
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^([^/.]+)$ $1.php [L,NS]

However, I'd like direct requests for "foo.php" come back with a 404 as though the file didn't exist (just as if someone tried "foo.asp" or "foo.blargh"). I know there's the [F] and [G] flags, but I don't want "forbidden" (403) or "gone" (410), as those are different results from what a nonexistent file returns (404).

Is there a way to do this?

UPDATE 1

I've tried

RewriteCond %{REQUEST_FILENAME} \.php$
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /.*\.php\ HTTP/
RewriteRule ^(.*)$ /error.php?e=404 [R=404,L,NS]

Which works... until I try to use an ErrorDocument handler. Assuming "error.php" at my document root, anything along the lines of ErrorDocument 404 /error?e=404 (i've also tried with error.php, etc) regurgitates the error

Not Found

The requested URL /foo.php was not found on this server.

Additionally, a 500 Internal Server Error error was encountered while trying to use an ErrorDocument to handle the request.

... but only when attempting to access a .php page that actually exists (such as foo.php); any attempt to access a page that actually doesn't exist (and thus doesn't trigger my second rewrite rule) goes to the page specified by the ErrorDocument handler.

回答1:

Found a solution:

RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ (/.*)\.php
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{REQUEST_URI} !^/error\.php
RewriteRule . - [R=404,L,NS]

ErrorDocument 404 /error.php?e=404

Specifically, the third RewriteCond keeps requests for the error page from triggering the RewriteRule (since the THE_REQUEST still contains the original request (matching the first RewriteCond), and the error page does indeed exist (matching the second RewriteCond).

Note that the third RewriteCond matches the URI format specified for the ErrorDocument. Within error.php, I use $_SERVER["REDIRECT_SCRIPT_URL"] to find out what file the user originally requested (thus making it look like the 404 was served as a direct response to their request).