Deny direct access to files or directory using .htaccess
Solution 1
If I've understood your requirements correctly, you're looking to do something like this:
# This is a real directory...
RewriteCond %{REQUEST_FILENAME} -f [OR]
# Or it's a real file...
RewriteCond %{REQUEST_FILENAME} -d
# And it's not 404.php...
RewriteCond $0 !=404.php
# And it's not the root
RewriteCond $0 !=""
# And it's not any of the above due to an internal redirect...
RewriteCond %{ENV:REDIRECT_STATUS} ^$
# So cause a 404 response (you could redirect to 404.php if you want)
RewriteRule ^.*$ - [R=404,L]
# Set the 404 error document
ErrorDocument 404 /~my_user/my_base/404.php
Keep in mind that this blocks everything that exists, so any images, stylesheets, or scripts will be sent to the 404 page too. If you just want to block access to the PHP files, Gumbo's solution is more appropriate. I think in that case you'll need another RewriteCond
though to prevent looping:
# Make sure the reason this request has a .php is because it was requested
# by the user (and not due to a redirect)
RewriteCond %{THE_REQUEST} ^[A-Z]+\s/[^\s]+\.php
# Make sure we aren't on 404.php already
RewriteRule %{REQUEST_URI} !404\.php$
# We aren't, so redirect to 404.php
RewriteRule ^ 404.php [L]
Solution 2
Try this rule:
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /[^?\ ]*\.php[/?\ ]
RewriteRule .*\.php$ 404.php [L]
This will rewrite all requests whose paths contain a .php
internally to 404.php.
Comments
-
dierre almost 2 years
I'm playing with
.htaccess
and I was wondering if with just an .htaccess inside the root directory is possible to block all the request from a browser directed on existing files or directories.Let's try this example:
RewriteEngine On RewriteBase /~my_user/my_base/ RewriteRule ^list/$ list.php [L] RewriteRule ^element_of_list/([a-zA-Z0-9\-]+)/$ element.php?elem_id=$1 [L]
Now, if I write
http://127.0.0.1/~my_user/my_base/list/
, this is wroking fine but if I writehttp://127.0.0.1/~my_user/my_base/list.php
it's still working. I don't want that. I want the user to obtain a 404 error in the last case.We have /etc/apache2/mods-enabled/userdir.conf
<IfModule mod_userdir.c> UserDir public_html UserDir disabled root <Directory /home/*/public_html> AllowOverride All Options Indexes FollowSymLinks <Limit GET POST OPTIONS> Order allow,deny Allow from all </Limit> <LimitExcept GET POST OPTIONS> Order deny,allow Deny from all </LimitExcept> </Directory> </IfModule>
My first try was to use
RewriteCond
:RewriteCond %{REQUEST_FILENAME} -f [OR] RewriteCond %{REQUEST_FILENAME} -d RewriteRule ^(.*)$ 404.php [L]
But it's not working. Every request ends up redirected to 404.php
UPDATE
So I've managed to create the filter for directories:
RewriteCond %{REQUEST_FILENAME} -d RewriteCond %{REQUEST_URI} !^/~my_user/my_base/$ [NC] RewriteRule ^(.*)$ 404.php [L]
What it does is to check if the requested path (
REQUEST_FILENAME
) exists and it's a directory AND if it's not my RewriteBase which is basically index.php, then redirect to 404.phpI'm still trying to find something that does the same thing for files. I know I can selectively do that using extensions filename but I want an universal filter for files.
-
dierre over 13 yearsThat's ok, but what about directory? Is it possible to do it without using a php file to check them? The -d flag is quite weird. I think I'm not getting it.
-
dierre over 13 yearsActually, why do you need the RewriteCond? What about RewriteRule ^(.*)\.php$ 404.php [L]
-
dierre over 13 yearsYeah I want to block everything but just out of curiosity. Could you explain me the use of
$0
in there? Does it contain${REQUEST_FILENAME}
? And you chained 5RewriteCond
, how does apache understand you need to confront the last three with the result of the first two? Does it just evaluate the first two before everything (in order of appearance)? -
Tim Stone over 13 years@dierre: The
$0
is a backreference to theRewriteRule
test pattern (it contains the entire match from^.*$
). It would have been possible to condense those twoRewriteCond
into the test pattern of theRewriteRule
, I suppose, like!^(|404\.php)$
. As for the chaining, there is an implicitAND
that joins allRewriteCond
together, unless you specifiy the[OR]
flag. -
Tim Stone over 13 yearsRelatedly, mod_rewrite processes the test pattern of the
RewriteRule
first, then tests eachRewriteCond
that is attached to the current rule (directly above it), starting at the top and working downwards. If all of that passes, then it performs the substitution in theRewriteRule
. -
dierre over 13 yearsI was thinking, you use in
ErrorDocument
the full path ofRewriteBase
, is there any env:variable containing the value ofRewriteBase
? -
Tim Stone over 13 years@dierre: Not that I'm aware of. There's also not any way to have
ErrorDocument
use the file relative to the current directory, as far as I know. In that case, it might be better to just redirect to 404.php directly, like theRewriteRule
in the second example, since then you wouldn't need to write the whole path again. Just be sure that 404.php returns a 404 status header in that case.