I want to deny direct access to a file but provide the file with RewriteRule
RewriteRule ^/image/([^/]+).jpg$ /image.php?name=$1
RewriteRule ^/image.php [L, R=404]
I expect localhost/image.php to return error 404 but it returns 404 also for localhost/image/image.jpg
The order of directives matters!
Specifically, the mod_rewrite
Apache module processes rules in order on each request, and later rules see the URL manipulated by earlier rules.
This is in fact what the [L]
flag you have used is for - it is short for [last]
, and means "don't process any more rules for this request, in the current context". A slightly stronger form is [END]
, meaning "don't process any more rules for this request, even if the new URL would re-process an .htaccess
file or <Directory>
block".
In your current rules, here's what happens to a request for /image/hello.jpg
:
^/image/([^/]+).jpg$
is tested against /image/hello.jpg
, matches, and rewrites the URL to /image.php?name=hello
^/image.php
is tested against the rewritten URL, /image.php?name=hello
, matches, and issues a 404If you put them the other way around:
RewriteRule ^/image.php [L, R=404]
RewriteRule ^/image/([^/]+).jpg$ /image.php?name=$1
Now, here's what happens to a request for /image/hello.jpg
:
^/image.php
is tested against /image/hello.jpg
, /image.php?name=hello
, doesn't match, so no change is applied^/image/([^/]+).jpg$
is tested against /image/hello.jpg
, matches, and rewrites the URL to /image.php?name=hello
.htaccess
file which will be re-processed, repeating step 1If you also use [END]
on both rules:
RewriteRule ^/image.php [END, R=404]
RewriteRule ^/image/([^/]+).jpg$ /image.php?name=$1 [END]
Now, here's what happens to a request for /image/hello.jpg
:
^/image.php
is tested against /image/hello.jpg
, /image.php?name=hello
, doesn't match, so no change is applied^/image/([^/]+).jpg$
is tested against /image/hello.jpg
, matches, and rewrites the URL to /image.php?name=hello
[END]
flag ensures that no more rules are processedIn fact, adding [END]
is enough in this case for it to work in either order:
RewriteRule ^/image/([^/]+).jpg$ /image.php?name=$1 [END]
RewriteRule ^/image.php [END, R=404]
^/image/([^/]+).jpg$
is tested against /image/hello.jpg
, matches, and rewrites the URL to /image.php?name=hello
[END]
flag ensures that no more rules are processed, so the ^/image.php
rule isn't even tested