nginx內置變量詳解


nginx的配置文件中可以使用的內置變量以美元符$開始,也有人叫全局變量。其中,部分預定義的變量的值是可以改變的。

$arg_PARAMETER 這個變量值為:GET請求中變量名PARAMETER參數的值。

$args 這個變量等於GET請求中的參數。例如,foo=123&bar=blahblah;這個變量只可以被修改

$binary_remote_addr 二進制碼形式的客戶端地址。

$body_bytes_sent 傳送頁面的字節數

$content_length 請求頭中的Content-length字段。

$content_type 請求頭中的Content-Type字段。

$cookie_COOKIE cookie COOKIE的值。

$document_root 當前請求在root指令中指定的值。

$document_uri $uri相同。

$host 請求中的主機頭(Host)字段,如果請求中的主機頭不可用或者空,則為處理請求的server名稱(處理請求的server的server_name指令的值)。值為小寫,不包含端口。

$hostname  機器名使用 gethostname系統調用的值

$http_HEADER   HTTP請求頭中的內容,HEADER為HTTP請求中的內容轉為小寫,-變為_(破折號變為下划線),例如:$http_user_agent(Uaer-Agent的值), $http_referer...;

$sent_http_HEADER  HTTP響應頭中的內容,HEADER為HTTP響應中的內容轉為小寫,-變為_(破折號變為下划線),例如: $sent_http_cache_control, $sent_http_content_type...;

$is_args 如果$args設置,值為"?",否則為""。

$limit_rate 這個變量可以限制連接速率。

$nginx_version 當前運行的nginx版本號。

$query_string $args相同。

$remote_addr 客戶端的IP地址。

$remote_port 客戶端的端口。

$remote_user 已經經過Auth Basic Module驗證的用戶名。

$request_filename 當前連接請求的文件路徑,由root或alias指令與URI請求生成。

$request_body 這個變量(0.7.58+)包含請求的主要信息。在使用proxy_pass或fastcgi_pass指令的location中比較有意義。

$request_body_file 客戶端請求主體信息的臨時文件名。

$request_completion 如果請求成功,設為"OK";如果請求未完成或者不是一系列請求中最后一部分則設為空。

$request_method 這個變量是客戶端請求的動作,通常為GET或POST。

包括0.8.20及之前的版本中,這個變量總為main request中的動作,如果當前請求是一個子請求,並不使用這個當前請求的動作。

$request_uri 這個變量等於包含一些客戶端請求參數的原始URI,它無法修改,請查看$uri更改或重寫URI。

$scheme 所用的協議,比如http或者是https,比如rewrite ^(.+)$ $scheme://example.com$1 redirect;

$server_addr 服務器地址,在完成一次系統調用后可以確定這個值,如果要繞開系統調用,則必須在listen中指定地址並且使用bind參數。

$server_name 服務器名稱。

$server_port 請求到達服務器的端口號。

$server_protocol 請求使用的協議,通常是HTTP/1.0或HTTP/1.1。

$uri 請求中的當前URI(不帶請求參數,參數位於$args),不同於瀏覽器傳遞的$request_uri的值,它可以通過內部重定向,或者使用index指令進行修改。不包括協議和主機名,例如/foo/bar.html

變量:

set $a "hello world"; 

我們使用了標准  ngx_rewrite 模塊的  set 配置指令對變量 $a 進行了賦值操作。我們把字符串 hello world 賦給了它

 

set $a hello;
set $b "$a, $a";

這里我們通過已有的 Nginx 變量 $a 的值,來構造變量 $b 的值,於是這兩條指令順序執行完之后,$a 的值是 hello,而 $b 的值則是 hello, hello.

 

 server {
        listen 8080;

        location /test {
            set $foo hello;
            echo "foo: $foo";
        }
    }

 

 $ curl 'http://localhost:8080/test'
    foo: hello

 

 

 

 

 

geo $dollar {
        default "$";
    }

    server {
        listen 8080;

        location /test {
            echo "This is a dollar sign: $dollar";
        }
    }

測試結果如下:

    $ curl 'http://localhost:8080/test'
    This is a dollar sign: $

這里用到了標准模塊  ngx_geo 提供的配置指令  geo 來為變量 $dollar 賦予字符串 "$",這樣我們在下面需要使用美元符的地方,就直接引用我們的 $dollar 變量就可以了。其實  ngx_geo 模塊最常規的用法是根據客戶端的 IP 地址對指定的 Nginx 變量進行賦值,這里只是借用它以便“無條件地”對我們的 $dollar 變量賦予“美元符”這個值。

 

 

即當引用的變量名之后緊跟着變量名的構成字符時(比如后跟字母、數字以及下划線),我們就需要使用特別的記法來消除歧義,例如:

    server {
        listen 8080;

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

這里,我們在  echo 配置指令的參數值中引用變量 $first 的時候,后面緊跟着 world 這個單詞,所以如果直接寫作 "$firstworld" 則 Nginx “變量插值”計算引擎會將之識別為引用了變量 $firstworld. 為了解決這個難題,Nginx 的字符串記法支持使用花括號在 $ 之后把變量名圍起來,比如這里的 ${first}. 

 

 

server {
        listen 8080;

        location /foo {
            echo "foo = [$foo]";
        }

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

這里我們在 location /bar 中用 set 指令創建了變量 $foo,於是在整個配置文件中這個變量都是可見的,因此我們可以在 location /foo 中直接引用這個變量而不用擔心 Nginx 會報錯

 

 

server {
        listen 8080;

        location /foo {
            set $a hello;
            echo_exec /bar;
        }

        location /bar {
            echo "a = [$a]";
        }
    }

既然是內部跳轉,當前正在處理的請求就還是原來那個,只是當前的 location 發生了變化,所以還是原來的那一套 Nginx 變量的容器副本。對應到上例,如果我們請求的是 /foo 這個接口,那么整個工作流程是這樣的:先在 location /foo 中通過  set 指令將 $a 變量的值賦為字符串 hello,然后通過  echo_exec 指令發起內部跳轉,又進入到 location /bar 中,再輸出 $a 變量的值。因為 $a 還是原來的 $a,所以我們可以期望得到 hello 這行輸出

 

 

模塊提供的內建變量  $uri,可以用來獲取當前請求的 URI(經過解碼,並且不含請求參數),而  $request_uri 則用來獲取請求最原始的 URI (未經解碼,並且包含請求參數)。請看下面這個例子:

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

這里為了簡單起見,連 server 配置塊也省略了,和前面所有示例一樣,我們監聽的依然是 8080 端口。在這個例子里,我們把  $uri 和  $request_uri 的值輸出到響應體中去。下面我們用不同的請求來測試一下這個 /test 接口:

    $ 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

 

 

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

然后在命令行上使用各種參數組合去請求這個 /test 接口:

    $ 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,等等:

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

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

Nginx 會在匹配參數名之前,自動把原始請求中的參數名調整為全部小寫的形式。

如果你想對 URI 參數值中的 %XX 這樣的編碼序列進行解碼,可以使用第三方  ngx_set_misc 模塊提供的  set_unescape_uri 配置指令:

    location /test {
        set_unescape_uri $name $arg_name;
        set_unescape_uri $class $arg_class;

        echo "name: $name";
        echo "class: $class";
    }

現在我們再看一下效果:

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

 

 

 location /main {
        echo_location /foo;
        echo_location /bar;
    }

    location /foo {
        echo foo;
    }

    location /bar {
        echo bar;
    }

這里在 location /main 中,通過第三方  ngx_echo 模塊的  echo_location 指令分別發起到 /foo 和 /bar 這兩個接口的 GET 類型的“子請求”。由  echo_location 發起的“子請求”,其執行是按照配置書寫的順序串行處理的,即只有當 /foo 請求處理完畢之后,才會接着處理 /bar 請求。這兩個“子請求”的輸出會按執行順序拼接起來,作為 /main 接口的最終輸出:

    $ curl 'http://localhost:8080/main'
    foo
    bar

 

 

location /main {
        set $var main;

        echo_location /foo;
        echo_location /bar;

        echo "main: $var";
    }

    location /foo {
        set $var foo;
        echo "foo: $var";
    }

    location /bar {
        set $var bar;
        echo "bar: $var";
    }

在這個例子中,我們分別在 /main,/foo 和 /bar 這三個 location 配置塊中為同一名字的變量,$var,分別設置了不同的值並予以輸出。特別地,我們在 /main 接口中,故意在調用過 /foo 和 /bar 這兩個“子請求”之后,再輸出它自己的 $var 變量的值。請求 /main 接口的結果是這樣的:

    $ curl 'http://localhost:8080/main'
    foo: foo
    bar: bar
    main: main

顯然,/foo 和 /bar 這兩個“子請求”在處理過程中對變量 $var 各自所做的修改都絲毫沒有影響到“主請求” /main. 

 

 

 location /main {
        set $var main;
        auth_request /sub;
        echo "main: $var";
    }

    location /sub {
        set $var sub;
        echo "sub: $var";
    }

這里我們在 /main 接口中先為 $var 變量賦初值 main,然后使用 ngx_auth_request 模塊提供的配置指令 auth_request,發起一個到 /sub 接口的“子請求”,最后利用  echo 指令輸出變量 $var 的值。而我們在 /sub 接口中則故意把 $var 變量的值改寫成 sub. 訪問 /main 接口的結果如下:

    $ curl 'http://localhost:8080/main'
    main: sub

我們看到,/sub 接口對 $var 變量值的修改影響到了主請求 /main. 所以 ngx_auth_request 模塊發起的“子請求”確實是與其“父請求”共享一套 Nginx 變量的值容器。

 

 

 

 

 

  location /main {
        echo "main args: $args";
        echo_location /sub "a=1&b=2";
    }

    location /sub {
        echo "sub args: $args";
    }

這里在 /main 接口中,先用  echo 指令輸出當前請求的  $args 變量的值,接着再用  echo_location 指令發起子請求 /sub. 這里值得注意的是,我們在  echo_location 語句中除了通過第一個參數指定“子請求”的 URI 之外,還提供了第二個參數,用以指定該“子請求”的 URL 參數串(即 a=1&b=2)。最后我們定義了 /sub 接口,在里面輸出了一下  $args 的值。請求 /main 接口的結果如下:

    $ curl 'http://localhost:8080/main?c=3'
    main args: c=3
    sub args: a=1&b=2

顯然,當  $args 用在“主請求” /main 中時,輸出的就是“主請求”的 URL 參數串,c=3;而當用在“子請求” /sub 中時,輸出的則是“子請求”的參數串,a=1&b=2

 location /main {
        echo_location /sub;
        echo "main method: $request_method";
    }

    location /sub {
        echo "sub method: $request_method";
    }

讓我們重新測試一下:

    $ curl --data hello 'http://localhost:8080/main'
    sub method: POST
    main method: POST

 

 

 

 location /main {
        echo "main method: $echo_request_method";
        echo_location /sub;
    }

    location /sub {
        echo "sub method: $echo_request_method";
    }

此時的輸出終於是我們想要的了:

    $ curl --data hello 'http://localhost:8080/main'
    main method: POST
    sub method: GET

 

 

 

 

 

 

 

  map $uri $tag {
        default     0;
        /main       1;
        /sub        2;
    }

    server {
        listen 8080;

        location /main {
            auth_request /sub;
            echo "main tag: $tag";
        }

        location /sub {
            echo "sub tag: $tag";
        }
    }

這里我們使用久違了的  map 指令來把內建變量  $uri 的值映射到用戶變量 $tag 上。當  $uri 的值為 /main 時,則賦予 $tag 值 1,當  $uri 取值 /sub 時,則賦予 $tag 值 2,其他情況都賦 0. 接着,我們在 /main接口中先用 ngx_auth_request 模塊的 auth_request 指令發起到 /sub 接口的子請求,然后再輸出變量 $tag 的值。而在 /sub 接口中,我們直接輸出變量 $tag.

 $ curl 'http://localhost:8080/main'
    main tag: 2

 

 

location /foo {
        echo "foo = [$foo]";
    }

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

這里為了簡單起見,省略了原先寫出的外圍 server 配置塊。在這個例子里,我們在 /bar 接口中用  set 指令隱式地創建了 $foo 變量這個名字,然后我們在 /foo 接口中不對 $foo 進行初始化就直接使用  echo指令輸出。我們當時測試 /foo 接口的結果是

    $ curl 'http://localhost:8080/foo'
    foo = []

從輸出上看,未初始化的 $foo 變量確實和空字符串的效果等同

 

 

 location /test {
        content_by_lua '
            if ngx.var.arg_name == nil then
                ngx.say("name: missing")
            else
                ngx.say("name: [", ngx.var.arg_name, "]")
            end
        ';
    }

這個例子和前一個例子功能上非常接近,除了我們在 /test 接口中使用了  ngx_lua 模塊的  content_by_lua 配置指令,嵌入了一小段我們自己的 Lua 代碼來對 Nginx 變量 $arg_name 的特殊值進行判斷。在這個例子中,當 $arg_name 的值為“沒找到”(或者“不合法”)時,/foo 接口會輸出 name: missing 這一行結果:

    $ curl 'http://localhost:8080/test'
    name: missing

 

 

 

 location /test {
        content_by_lua '
            if ngx.var.cookie_user == nil then
                ngx.say("cookie user: missing")
            else
                ngx.say("cookie user: [", ngx.var.cookie_user, "]")
            end
        ';
    }

利用 curl 命令行工具的 --cookie name=value 選項可以指定 name=value 為當前請求攜帶的 cookie(通過添加相應的 Cookie 請求頭)。下面是若干次測試結果:

    $ curl --cookie user=agentzh 'http://localhost:8080/test'
    cookie user: [agentzh]

    $ curl --cookie user= 'http://localhost:8080/test'
    cookie user: []

    $ curl 'http://localhost:8080/test'
    cookie user: missing

我們看到,cookie user 不存在以及取值為空字符串這兩種情況被很好地區分開了:當 cookie user 不存在時,Lua 代碼中的 ngx.var.cookie_user 返回了期望的 Lua nil 值

 

 

 location /test {
        content_by_lua '
            ngx.say("$blah = ", ngx.var.blah)
        ';
    }

這里假設我們並沒有在當前的 nginx.conf 配置文件中創建過用戶變量 $blah,然后我們在 Lua 代碼中通過 ngx.var.blah 直接引用它。上面這個配置可以順利啟動,因為 Nginx 在加載配置時只會編譯 content_by_lua 配置指令指定的 Lua 代碼而不會實際執行它,所以 Nginx 並不知道 Lua 代碼里面引用了 $blah 這個變量。於是我們在運行時也會得到 nil 值。而  ngx_lua 提供的  ngx.say 函數會自動把 Lua 的 nil 值格式化為字符串 "nil" 輸出,於是訪問 /test 接口的結果是:

    curl 'http://localhost:8080/test'
    $blah = nil

 

 

 location /foo {
        content_by_lua '
            if ngx.var.foo == nil then
                ngx.say("$foo is nil")
            else
                ngx.say("$foo = [", ngx.var.foo, "]")
            end
        ';
    }

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

請求 /foo 接口的結果是:

    $ curl 'http://localhost:8080/foo'
    $foo = []

我們看到在 Lua 里面讀取未初始化的 Nginx 變量 $foo 時得到的是空字符串。

 

 

ocation /test {
        array_split "," $arg_names to=$array;
        array_map "[$array_it]" $array;
        array_join " " $array to=$res;

        echo $res;
    }

這個例子中使用了  ngx_array_var 模塊的 array_split array_map 和 array_join 這三條配置指令,其含義很接近 Perl 語言中的內建函數 split、map 和 join(當然,其他腳本語言也有類似的等價物)。我們來看看訪問 /test 接口的結果:

    $ curl 'http://localhost:8080/test?names=Tom,Jim,Bob'
    [Tom] [Jim] [Bob]

 

 

Nginx 配置指令的執行順序

按照它們執行時的先后順序,依次是 rewrite 階段、access 階段以及 content 階段,  set 指令就是在 rewrite 階段運行的,而  echo 指令就只會在 content 階段運行。前面我們已經知道,在單個請求的處理過程中,rewrite 階段總是在 content 階段之前執行,因此屬於 rewrite 階段的配置指令也總是會無條件地在 content 階段的配置指令之前執行。於是在同一個 location 配置塊中, set 指令總是會在 echo 指令之前執行,即使我們在配置文件中有意把  set 語句寫在  echo 語句的后面。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM