preventing rewrite loops with "index"

Dennis J. dennisml at
Thu Jan 28 06:15:14 MSK 2010

On 01/25/2010 11:57 AM, Maxim Dounin wrote:
> Hello!
> On Mon, Jan 25, 2010 at 08:58:18AM +0200, Marcus Clyne wrote:
>> Hi,
>> Maxim Dounin wrote:
>>> Hello!
>>> On Sun, Jan 24, 2010 at 10:45:32PM +0100, Piotr Sikora wrote:
>>>>> 2. Note "internal" in location /users/.  It means "only visible
>>>>> for internal redirects", so even user called "users" should be
>>>>> correctly processed by the first location.
>>>> Actually, this isn't true. Any attempt to access internal location
>>>> results in 404 response.
>>>> You can verify this with very simple configuration:
>>>> server {
>>>>    listen 8000;
>>>>    location / { return 500; }
>>>>    location /x { internal; return 500; }
>>>> }
>>>> Accessing /x will result in 404 response.
>> The example is obviously correct, but it doesn't truly explain the
>> reason for getting the 404 for accessing /users/xxx URLs (even
>> though the result is almost the same).  The reason is to do with the
>> order that locations are handled, specifically that ^~ locations are
>> handled before ~* and ~ ones, and if they match, then the regex ones
>> aren't tested.  If you try to access the URL /users/xxx, it will
>> therefore match the second location given by ^~, and return 404
>> because it's an internal location.  Therefore, trying access
>> anything under a user named 'users' will fail (though the URL /users
>> on its own is ok, because that will match the regex location and not
>> the ^~ location).
> It's somewhat obvious.
>> Using location /users in the original locations will result in an
>> internal server error, because the regex will be caught before the
>> /users location each time the URL is checked, creating an infinite
>> loop.
> By "original" you mean config I'm suggested to Dennis J?  No, as
> first rewrite will add '/' to it, and on next iteration it will be
> caught by /users/.
> The problem will arise with directory redirects though
> (/username/dir ->  /username/dir/), as they will use paths after
> rewrites, and this isn't what we need here.  When user has dir in
> it's htdocs - wee need redirect "/user/dir" ->  "/user/dir/", but
> the config will issue "/users/u/s/e/users/dir/" one.
>> From the above I think that using alias will be better.  In
> 0.8.* this may be done with named captures and nested locations,
> like this:
>     location ~* ^/(?<name>(?<n1>[a-z])(?<n2>[a-z0-9])(?<n3>[a-z0-9])[^/]*)(?<p>/.*)?$ {
>         alias /tmp/users/$n1/$n2/$n3/$name/htdocs$p;
>         location ~ \.(mpg|zip|avi)$ {
>             valid_referers localhost none blocked;
>             if ($invalid_referer) {
>                 return 403;
>             }
>         }
>     }

I now went with something like the above but I'm still running into a snag 
when I get to setting up fastCGI which apparently has to do with the fact 
that $document_root is not set up properly when "alias" is used. This is my 
config so far:

location ~* 
^/(?<name>(?<n1>[a-z])(?<n2>[a-z0-9])(?<n3>[a-z0-9])[^/]*)(?<p>/.*)?$ {
     alias /web/users/$n1/$n2/$n3/$name/htdocs$p;

     if (-f /web/users/$n1/$n2/$n3/$name/user/.disable-member) {
         return 405;

     location ~ \.(zip|mpg|avi)$ {
         valid_referers none;
         if ($invalid_referer) {
             return 403;

     location ~ \.php$ {
         set $phphost;
         fastcgi_pass   $phphost;
         fastcgi_index  index.php;
         fastcgi_param  DOCUMENT_ROOT /web/users/$n1/$n2/$n3/$name/htdocs;
         fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
         fastcgi_param  PATH_INFO  $fastcgi_script_name;
         include        fastcgi_params;

Notice how I have to set DOCUMENT_ROOT and SCRIPT_FILENAME in order to get 
this working. What is strange is that $document_root is 
"/web/users/t/e/s/test/htdocs/index.php" (the alias?) and 
$fastcgi_script_name is "/test/index.php" yet when I call a script 
displaying $_SERVER I get SCRIPT_FILENAME displayed as 
"/web/users/t/e/s/test/htdocs/index.php" which is what I want but according 
to the definition above I shouldn't get. What I would expect is 
"/web/users/t/e/s/test/htdocs/index.php/test/index.php" given the values of 
the variables.
Also even if these are right I still get ORIG_SCRIPT_FILENAME as 
"/web/users/t/e/s/test/htdocs/index.php/test/index.php", PATH_TRANSLATED as 
"/web/users/t/e/s/test/htdocs/test/index.php" and PHP_SELF as 
"/test/index.php/test/index.php" which all don't look right.

Any ideas why the variables look messed up like this?


More information about the nginx mailing list