变量插值:

所有的 Nginx变量在 Nginx配置文件中引用时都须带上 $前缀,用$符号+变量名来构造新的变量,这种方式叫做变量插值。
例如:


location test{
    set $a hello;
    set $b "$a ,world";
    echo "b: $b";
}



访问:

curl  http://localhost/test/


输出:b: hello world
说明:上述set是HttpRewriteModule的指令,而echo是HttpEchoModule指令。可见这两个模块都支持变量插值。
但是并不是所有模块都支持变量插值,事实上,指令参数是否允许“变量插值”,取决于该指令的实现模块。

变量使用注意点:

当变量后面直接接其他字符串时,需要用{}将变量名括起来,否则会将变量名和后面的字符串连在一起当成一个变量名
例如:


location /test {
    set $variable "hello";
    echo"${variable}world";
}


变量必须先创建再使用,否则报错
例如:


server {
    listen 80;
    location /test
    {
        echo $foo;
     }
}



访问:

curl  http://localhost/test/


报错:[emerg] unknown"foo" variable
说明:nginxnginx变量的创建和赋值操作发生在全然不同的时间阶段。Nginx变量的创建只能发生在 Nginx配置加载的时候,或者说 Nginx启动的时候;而赋值操作则只会发生在请求实际处理的时候。这意味着不创建而直接使用变量会导致启动失败,同时也意味着我们无法在请求处理时动态地创建新的 Nginx变量

变量的可见性和作用域:


Nginx 变量一旦创建,其变量名的可见范围就是整个 Nginx 配置,甚至可以跨越不同虚拟主机的 server 配置块。

例如:

server {
    listen 80;
 
    location /foo {
        echo "foo =[$foo]";
    }
 
    location /bar {
        set $foo 32;
        echo "foo =[$foo]";
    }
}



说明:这里我们在

location /bar 中用 set 指令创建了变量 $foo ,于是在整个配置文件中这个变量都是可见的,因此我们可以在 location /foo 中直接引用这个变量而不用担心 Nginx 会报错。

访问这两个接口的结果:


$ curl 'http://localhost/foo'
foo = []
 
$ curl 'http://localhost/bar'
foo = [32]
 
$ curl 'http://localhost/foo'
foo = []



从本例子得出另一个变量特性:


Nginx 变量名的可见范围虽然是整个配置,但每个请求都有所有变量的独立副本,或者说都有各变量用来存放值的容器的独立副本,彼此互不干扰。
比如前面我们请求了 /bar接口后,$foo变量被赋予了值 32,但它丝毫不会影响后续对 /foo接口的请求所对应的 $foo值(它仍然是空的!),因为各个请求都有自己独立的 $foo变量的副本
对于 Nginx新手来说,最常见的错误之一,就是将 Nginx变量理解成某种在请求之间全局共享的东西,或者说“全局变量”。而事实上,Nginx变量的生命期是不可能跨越请求边界的

另外一个误区是:认为变量容器的生命期,是与 location 配置块绑定的。其实不然。我们来看一个涉及“内部跳转”的例子

server {
    listen 8080;
 
    location /foo {
        set $a hello;
        echo_exec /bar;
    }
 
    location /bar {
        echo "a = [$a]";
    }
}




这里我们在 location /foo 中,使用第三方模块ngx_echo提供的echo_exec配置指令,发起到location /bar 的“内部跳转”。所谓“内部跳转”,就是在处理请求的过程中,于服务器内部,从一个 location 跳转到另一个 location 的过程。这不同于利用 HTTP 状态码 301 和 302 所进行的“外部跳转”,因为后者是由 HTTP 客户端配合进行跳转的,而且在客户端,用户可以通过浏览器地址栏这样的界面,看到请求的 URL 地址发生了变化。内部跳转和 Bourne Shell(或 Bash)中的exec命令很像,都是“有去无回”。另一个相近的例子是C语言中的goto语句。

既然是内部跳转,当前正在处理的请求就还是原来那个,只是当前的location发生了变化,所以还是原来的那一套 Nginx 变量的容器副本。对应到上例,如果我们请求的是/foo这个接口,那么整个工作流程是这样的:先在location /foo中通过set指令将 $a 变量的值赋为字符串 hello,然后通过echo_exec指令发起内部跳转,又进入到location /bar中,再输出$a变量的值。因为 $a 还是原来的 $a,所以我们可以期望得到hello这行输出。测试证实了这一点。

$ curl localhost:8080/foo
a = [hello]

但如果我们从客户端直接访问

/bar接口,就会得到空的

$a变量的值,因为它依赖于

location /foo来对

$a进行初始化。

从上面这个例子我们看到,一个请求在其处理过程中,即使经历多个不同的 location 配置块,它使用的还是同一套 Nginx 变量的副本

这里,我们也首次涉及到了“内部跳转”这个概念。值得一提的是标准ngx_rewrite模块的rewrite配置指令其实也可以发起“内部跳转”,例如上面那个例子用rewrite配置指令可以改写成下面这样的形式:

server {
    listen 8080;
 
    location /foo {
        set $a hello;
        rewrite ^ /bar;
    }
 
    location /bar {
        echo "a = [$a]";
    }
}

其效果和使用 echo_exec 是完全相同的。后面我们还会专门介绍这个 rewrite 指令的更多用法,比如发起 301 和 302 这样的“外部跳转”。


内建变量


常见的内建变量:


$uri:

用来获取当前请求的URI(经过解码,并且不包含请求参数)
$request_uri:用来获取请求最原始的URI(未经解码,并且包含请求参数)

location /test {
    echo "uri = $uri";
    echo "request_uri =$request_uri";
}



请求和响应:

$ curl 'http://localhost:8080/test'
uri = /test
request_uri = /test
 
$ curl 'http://localhost:8080/test?a=3&b=4'
uri = /test
request_uri = /test?a=3&b=4
 
$ curl 'http://localhost:8080/test/hello%20world?a=3&b=4'
uri = /test/hello world
request_uri = /test/hello%20world?a=3&b=4


$arg_xxx变量群:用来获取当前请求名为xxx的URI参数的值


location /test {
    echo "name:$arg_name";
    echo "class:$arg_class";
}



请求和响应:

$ curl 'http://localhost:8080/test'
name:
class:
 
$ curl 'http://localhost:8080/test?name=Tom&class=3'
name: Tom
class: 3
 
$ curl 'http://localhost:8080/test?name=hello%20world&class=9'
name: hello%20world
class: 9

其实 $arg_name不仅可以匹配 name参数,也可以匹配 NAME参数,抑或是 Name,等等,Nginx会在匹配参数名之前,自动把原始请求中的参数名调整为全部小写的形式:



$ curl 'http://localhost:8080/test?NAME=Marry'
 name: Marry
class:
 
$ curl 'http://localhost:8080/test?Name=Jimmy'
name: Jimmy
class:

$cookie_xxx变量群:用来获取cookie值

$http_xxx变量群:用来获取请求头的值
$send_http_xxx变量群:用来获取响应头的值
$args:返回当前请求的URL参数串(即请求URL中问号后面的部分)

如果你尝试改写另外一些只读的内建变量,比如 $arg_XXX 变量,在某些 Nginx 的版本中甚至可能导致进程崩溃。

location /bad {
    set $uri /blah;
    echo $uri;
 }

这个有问题的配置会让 Nginx 在启动的时候报出一条令人匪夷所思的错误:


[emerg] the duplicate "uri" variable in ...

也有一些内建变量是支持改写的,其中一个例子是 $args. 这个变量在读取时返回当前请求的 URL 参数串(即请求 URL 中问号后面的部分,如果有的话 ),而在赋值时可以直接修改参数串

例如:

location /test {
    set $orig_args $args;
    set $args "a=3&b=4";
 
    echo "original args: $orig_args";
    echo "args: $args";
}

访问和输出:

$ curl 'http://localhost:8080/test'
original args: 
args: a=3&b=4
 
$ curl 'http://localhost:8080/test?a=0&b=1&c=2'
original args: a=0&b=1&c=2
args: a=3&b=4




$arg_XXX

这样具有无数变种的变量群,是“未索引的”。当读取这样的变量时,其实是它的“取处理程序”(get(xxx))在起作用,即实时扫描当前请求的 URL 参数串,提取出变量名所指定的 URL 参数的值。很多新手都会对

$arg_XXX的实现方式产生误解,以为 Nginx 会事先解析好当前请求的所有 URL 参数,并且把相关的$arg_XXX变量的值都事先设置好。然而事实并非如此,Nginx 根本不会事先就解析好 URL 参数串,而是在用户读取某个$arg_XXX

变量时,调用其“取处理程序”,即时去扫描 URL 参数串。类似地,内建变量

$cookie_XXX

也是通过它的“取处理程序”,即时去扫描

Cookie

请求头中的相关定义的。