Search code examples
.htaccessmod-rewrite

how to use htaccess to deny browser request url but not the RewriteRule?


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


Solution

  • 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:

    1. The pattern ^/image/([^/]+).jpg$ is tested against /image/hello.jpg, matches, and rewrites the URL to /image.php?name=hello
    2. The pattern ^/image.php is tested against the rewritten URL, /image.php?name=hello, matches, and issues a 404

    If 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:

    1. The pattern ^/image.php is tested against /image/hello.jpg, /image.php?name=hello, doesn't match, so no change is applied
    2. The pattern ^/image/([^/]+).jpg$ is tested against /image/hello.jpg, matches, and rewrites the URL to /image.php?name=hello
    3. However, there's a risk that these directives are in a .htaccess file which will be re-processed, repeating step 1

    If 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:

    1. The pattern ^/image.php is tested against /image/hello.jpg, /image.php?name=hello, doesn't match, so no change is applied
    2. The pattern ^/image/([^/]+).jpg$ is tested against /image/hello.jpg, matches, and rewrites the URL to /image.php?name=hello
    3. The [END] flag ensures that no more rules are processed

    In 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]
    
    1. The pattern ^/image/([^/]+).jpg$ is tested against /image/hello.jpg, matches, and rewrites the URL to /image.php?name=hello
    2. The [END] flag ensures that no more rules are processed, so the ^/image.php rule isn't even tested