Nginx 配置指令的執行順序(八)

2021-07-16 16:30:43 字數 3938 閱讀 3890

前面我們詳細討論了rewriteaccesscontent這三個最為常見的 nginx 請求處理階段,在此過程中,也順便介紹了執行在這三個階段的眾多 nginx 模組及其配置指令。同時可以看到,請求處理階段的劃分直接影響到了配置指令的執行順序,熟悉這些階段對於正確配置不同的 nginx 模組並實現它們彼此之間的協同工作是非常必要的。所以接下來我們接著討論餘下的那些階段。

前面在 (一) 中提到,nginx 處理請求的過程一共劃分為 11 個階段,按照執行順序依次是post-readserver-rewritefind-configrewritepost-rewritepreaccessaccesspost-accesstry-filescontent以及log.

最先執行的post-read階段在 nginx 讀取並解析完請求頭(request headers)之後就立即開始執行。這個階段像前面介紹過的rewrite階段那樣支援 nginx 模組註冊處理程式。比如標準模組 ngx_realip 就在post-read階段註冊了處理程式,它的功能是迫使 nginx 認為當前請求的**位址是指定的某乙個請求頭的值。下面這個例子就使用了 ngx_realip 模組提供的 set_real_ip_from 和 real_ip_header 這兩條配置指令:

server

}

這裡的配置是讓 nginx 把那些來自127.0.0.1的所有請求的**位址,都改寫為請求頭x-my-ip所指定的值。同時該例使用了標準內建變數 $remote_addr 來輸出當前請求的**位址,以確認是否被成功改寫。

首先在本地請求一下這個/test介面:

$ curl 

-h'x-my-ip: 1.2.3.4'

localhost:8080

/test

from: 1.2.3.4

這裡使用了 curl 工具的-h選項指定了額外的 http 請求頭x-my-ip: 1.2.3.4. 從輸出可以看到,$remote_addr 變數的值確實在rewrite階段就已經成為了x-my-ip請求頭中指定的值,即1.2.3.4. 那麼 nginx 究竟是在什麼時候改寫了當前請求的**位址呢?答案是:在post-read階段。由於rewrite階段的執行遠在post-read階段之後,所以當在location配置塊中通過 set 配置指令讀取 $remote_addr 內建變數時,讀出的**位址已經是經過post-read階段篡改過的。

如果在請求上例中的/test介面時沒有指定x-my-ip請求頭,或者提供的x-my-ip請求頭的值不是合法的 ip 位址,那麼 nginx 就不會對**位址進行改寫,例如:

$ curl localhost:8080

/test

from: 127.0.0.1

$ curl 

-h'x-my-ip: abc'

localhost:8080

/test

from: 127.0.0.1

如果從另一台機器訪問這個/test介面,那麼即使指定了合法的x-my-ip請求頭,也不會觸發 nginx 對**位址進行改寫。這是因為上例已經使用 set_real_ip_from 指令規定了**位址的改寫操作只對那些來自127.0.0.1的請求生效。這種過濾機制可以避免來自其他不受信任的位址的惡意欺騙。當然,也可以通過set_real_ip_from 指令指定乙個 ip 網段(利用 (三) 中介紹過的「cidr 記法」)。此外,同時配置多個set_real_ip_from 語句也是允許的,這樣可以指定多個受信任的**位址或位址段。下面是乙個例子:

set_real_ip_from

10.32.10.5;

set_real_ip_from

127.0.0.0/24

;

有的讀者可能會問,ngx_realip 模組究竟有什麼實際用途呢?為什麼我們需要去改寫請求的**位址呢?答案是:當 nginx 處理的請求經過了某個 http **伺服器的**時,這個模組就變得特別有用。當原始的使用者請求經過**之後,nginx 接收到的請求的**位址無一例外地變成了該**伺服器的 ip 位址,於是 nginx 以及 nginx 背後的應用就無法知道原始請求的真實**。所以,一般我們會在 nginx 之前的**伺服器中把請求的原始**位址編碼進某個特殊的 http 請求頭中(例如上例中的x-my-ip請求頭),然後再在 nginx 一側把這個請求頭中編碼的位址恢復出來。這樣 nginx 中的後續處理階段(包括 nginx 背後的各種後端應用)就會認為這些請求直接來自那些原始的位址,**伺服器就彷彿不存在一樣。正是因為這個需求,所以 ngx_realip 模組才需要在第乙個處理階段,即post-read階段,註冊處理程式,以便盡可能早地改寫請求的**。

post-read階段之後便是server-rewrite階段。我們曾在 (二) 中簡單提到,當 ngx_rewrite 模組的配置指令直接書寫在server配置塊中時,基本上都是執行在server-rewrite階段。下面就來看這樣的乙個例子:

server

set $a

hello;

}

這裡,配置語句set $a hello直接寫在了server配置塊中,因此它就執行在server-rewrite階段。而server-rewrite階段要早於rewrite階段執行,因此寫在location配置塊中的語句set $b "$a, world"便晚於外面的set $a hello語句執行。該例的測試結果證明了這一點:

$ curl localhost:8080

/test

hello, world

由於server-rewrite階段位於post-read階段之後,所以server配置塊中的 set 指令也就總是執行在ngx_realip 模組改寫請求的**位址之後。來看下面這個例子:

server

}

請求/test介面的結果如下:

$ curl 

-h'x-real-ip: 1.2.3.4'

localhost:8080

/test

from: 1.2.3.4

在這個例子中,雖然 set 指令寫在了 ngx_realip 的配置指令之前,但仍然晚於 ngx_realip 模組執行。所以$addr變數在server-rewrite階段被 set 指令賦值時,從 $remote_addr 變數讀出的**位址已經是經過改寫過的了。

Nginx 配置指令的執行順序(八)

前面我們詳細討論了rewrite access和content這三個最為常見的 nginx 請求處理階段,在此過程中,也順便介紹了執行在這三個階段的眾多 nginx 模組及其配置指令。同時可以看到,請求處理階段的劃分直接影響到了配置指令的執行順序,熟悉這些階段對於正確配置不同的 nginx 模組並實...

Nginx 配置指令的執行順序(七)

來看乙個ngx static模組服務磁碟檔案的例子。我們使用下面這個配置片段 location 同時在本機的 var www 目錄下建立兩個檔案,乙個檔案叫做index.html,內容是一行文字this is my home 另乙個檔案叫做hello.html,內容是一行文字hello world....

Nginx 配置指令的執行順序(十)

執行在post rewrite階段之後的是所謂的preaccess階段。該階段在access階段之前執行,故名preaccess.標準模組 ngx limit req 和 ngx limit zone 就執行在此階段,前者可以控制請求的訪問頻度,而後者可以限制訪問的併發度。這裡我們僅僅和它們打個照面...