Is this how variable (set $var) inheritance works?

(doesn’t seem to be specified in documentation)

My test showed:

  • anything set in server { } block is inherited:

server {
set $something /usr/share/something;

location / {
location /nested/ {
# $something is set to /usr/share/something
root $something;
}
}
}

  • anything set in location { } block is not inherited:

server {

location /~ {
location ~ ^/~([^/]+)(|/.*)$ {
set $userfile /home/$1/public_html/$2;
alias $userfile;
location ~ .php$ {
include fastcgi_params;
# $userfile is empty here
fastcgi_param SCRIPT_FILENAME $userfile;
fastcgi_pass 127.0.0.1:9000;
}
}
}

Is it correct?


O< ascii ribbon campaign - stop html mail - www.asciiribbon.org

Hello!

On Fri, Feb 03, 2012 at 10:26:07PM +0700, Edho A. wrote:

  # $something is set to /usr/share/something
location ~ ^/~([^/]+)(|/.*)$ {

Is it correct?

Yes, this is expected behavrious. Rewrite module directives are
never inherited, and directives specified inside location are only
executed if this exact location matches.

See here for details:
http://nginx.org/en/docs/http/ngx_http_rewrite_module.html

Maxim D.

On Sat, Feb 4, 2012 at 1:10 AM, Maxim D. [email protected] wrote:

Yes, this is expected behavrious. Rewrite module directives are
never inherited, and directives specified inside location are only
executed if this exact location matches.

See here for details:
Module ngx_http_rewrite_module

I replaced the line:

  -fastcgi_param SCRIPT_FILENAME $userfile;
  +fastcgi_param SCRIPT_FILENAME /home/edho/public_html/test.php;

But I still see this:

2012/02/03 13:30:16 [warn] 12889#0: *17 using uninitialized “userfile”
variable, client: 118.136.36.164, server: localhost, request: “GET
/~edho/test.php HTTP/1.1”, host: “yutsuki.myconan.net

Why did I get warning for using uninitialized variables even though
it’s not specified at all in the relevant location block?

Additionally:

Is there any variable I can use for my case? $request_filename
returned “/opt/nginx/” (bug?). Or do I have use separate block when
using alias (ie. nothing is inherited when using alias)?


O< ascii ribbon campaign - stop html mail - www.asciiribbon.org

On Sat, Feb 4, 2012 at 1:48 AM, Edho A. [email protected] wrote:

I replaced the line:

Why did I get warning for using uninitialized variables even though
it’s not specified at all in the relevant location block?

After a bit more thinking, does it mean that the “alias $userfile”
from parent location block was inherited but the $userfile was passed
literally?


O< ascii ribbon campaign - stop html mail - www.asciiribbon.org

On Sat, Feb 4, 2012 at 1:50 AM, Edho A. [email protected] wrote:

Why did I get warning for using uninitialized variables even though
it’s not specified at all in the relevant location block?

After a bit more thinking, does it mean that the “alias $userfile”
from parent location block was inherited but the $userfile was passed
literally?

More self-reply: it certainly looks like the $userfile is passed but
still in variable, not expanded with the contents as the following
test showed.

Skipping “set $userfile …” and put the capture in alias directly:

location ~ ^/~([^/]+)(|/.*)$ {
alias /home/$1/public_html/$2;
location ~ .php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_pass 127.0.0.1:9000;
}
}

resulted in this:

2012/02/03 13:51:28 [debug] 14951#0: *13 fastcgi param:
“REDIRECT_STATUS: 200”
2012/02/03 13:51:28 [debug] 14951#0: *13 http script copy: “/home/”
2012/02/03 13:51:28 [debug] 14951#0: *13 http script capture: “”
2012/02/03 13:51:28 [debug] 14951#0: *13 http script copy:
“/public_html/”
2012/02/03 13:51:28 [debug] 14951#0: *13 http script capture: “”
2012/02/03 13:51:28 [debug] 14951#0: *13 http script copy:
“SCRIPT_FILENAME”
2012/02/03 13:51:28 [debug] 14951#0: *13 http script var:
“/home//public_html/”
2012/02/03 13:51:28 [debug] 14951#0: *13 fastcgi param:
“SCRIPT_FILENAME: /home//public_html/”
2012/02/03 13:51:28 [debug] 14951#0: *13 fastcgi param: “HTTP_HOST:
yutsuki.myconan.net

Which explains why I got “/opt/nginx/” for $request_filename on
previous config - the .php$ location block was basically using alias $userfile but as $userfile was empty, it became empty alias for that
block.


O< ascii ribbon campaign - stop html mail - www.asciiribbon.org

On Sat, Feb 4, 2012 at 8:15 AM, Max [email protected] wrote:

This feature/bug is especially confusing when you use variables
inside root and alias directives, because the nested location blocks
will inherit the root and alias contents (unless specifically set),
which will have any uninitialized inherited variable names replaced
with “”, so “/home/$variable/abc/$dir/” would become “/home//abc//”.

At least this makes nested location useless for cases like this.
Instead of one regex with captures (and then use nested location), I
had to do this instead:

location /~ {
location ~ ^/~([^/]+)/(.+.php)$ {
alias /home/$1/public_html/$2;
if (!-f $request_filename) { return 404; }
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_pass 127.0.0.1:9000;
}
location ~ ^/~([^/]+)(|/.*)$ {
alias /home/$1/public_html/$2;
index index.html;
}
}

Or use map (and since it’s currently impossible to do non-simple regex
capture, I had to use two maps):

map $uri $user {
~^/~(?P[^/]+)(|/.)$ $user1;
}
map $uri $file {
~^/~[^/]+(?P|/.
)$ $file1;
}
server {

location /~ {
location ~ ^ {
alias /home/$user/public_html/$file;
location ~ .php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_pass 127.0.0.1:9000;
}
}
}
}


O< ascii ribbon campaign - stop html mail - www.asciiribbon.org

03 февраля 2012, 22:59 от Edho A. [email protected]:

More self-reply: it certainly looks like the $userfile is passed but
fastcgi_pass 127.0.0.1:9000;
2012/02/03 13:51:28 [debug] 14951#0: *13 http script copy: “SCRIPT_FILENAME”
2012/02/03 13:51:28 [debug] 14951#0: *13 http script var: “/home//public_html/”
2012/02/03 13:51:28 [debug] 14951#0: *13 fastcgi param:
“SCRIPT_FILENAME: /home//public_html/”
2012/02/03 13:51:28 [debug] 14951#0: *13 fastcgi param: “HTTP_HOST:
yutsuki.myconan.net

Which explains why I got “/opt/nginx/” for $request_filename on
previous config - the .php$ location block was basically using alias $userfile but as $userfile was empty, it became empty alias for that
block.

Nested location blocks inherit variables only by name, which means
the inherited variables are only created and marked as uninitialized.
Note that this is not the same as creating an empty variable that
contains “”. The distinction should be similar to the one between
defined vs. empty variables in Perl: if you set $variable to “”,
then ‘if ($variable)’ would evaluate to false, while
‘if (defined($variable))’ would evaluate to true.

This distinction exists in nginx as well, where ‘if ($variable)’
would also evaluate to false, but there’s no direct way to
test whether a variable is defined. However nginx tests for
this automatically - if you try to use an undefined variable,
nginx will report an ‘unknown “<variable_name>” variable’ error
and fail to start.

That’s why a variable that was set to a certain value inside the
outermost (parent) location block will be inherited by nested location
blocks only by name, which means that if you try to use such a
variable inside a nested location block, nginx won’t report any
errors, but instead of the original contents of the variable, you will
only get an empty string when you try to access the contents.

This feature/bug is especially confusing when you use variables
inside root and alias directives, because the nested location blocks
will inherit the root and alias contents (unless specifically set),
which will have any uninitialized inherited variable names replaced
with “”, so “/home/$variable/abc/$dir/” would become “/home//abc//”.

Another important thing to remember is that if you set root or alias
to contain nothing but a variable (root $home;), then the root inside
any nested location block where you do not set root or alias
specifically,
will be reset NOT to your default root value from the server
configuration block, but to the value of the prefix configure argument
that nginx was compiled with (/usr/local/etc/nginx), which you can see
under --prefix= when you run nginx -V.

Your nginx was probably compiled with --prefix=/opt/nginx/.

Max

On 4 Fev 2012 02h53 WET, [email protected] wrote:

At least this makes nested location useless for cases like this.
}
location ~ ^/~([^/]+)(|/.*)$ {
alias /home/$1/public_html/$2;
index index.html;
}
}

The thing is that there is a server level rewrite phase and a location
level rewrite phase. And since you can have only one and only one
location everything at the location rewrite phase that happens before
a redirect/rewrite gets lost. Why?

Because you enter a configuration find phase again and after that a
rewrite phase again on the location it was redirected to.

The server level rewrite only happens once in each vhost hence the
values of user variables set there are preserved.

That’s my understanding of it, at least.

— appa