Nginx 配置指令的執行順序(七)
來看一個 ngx_static
模塊服務磁盤文件的例子。我們使用下面這個配置片段:
location / {
root /var/www/;
}
同時在本機的 /var/www/
目錄下創建兩個文件,一個文件叫做 index.html
,內容是一行文本 this is my home
;另一個文件叫做 hello.html
,內容是一行文本 hello world
. 同時注意這兩個文件的權限設置,確保它們都對運行 Nginx worker 進程的系統帳戶可讀。
現在來通過 HTTP 協議請求一下這兩個文件所對應的 URI:
$ curl 'http://localhost:8080/index.html'
this is my home
$ curl 'http://localhost:8080/hello.html'
hello world
我們看到,先前創建的那兩個磁盤文件的內容被分別輸出了。
不妨來分析一下這里發生的事情:location /
中沒有使用運行在 content
階段的模塊指令,于是也就沒有模塊注冊這個 location
的“內容處理程序”,處理權便自動落到了在 content
階段“墊底”的那 3 個靜態資源服務模塊。首先運行的 ngx_index 和 ngx_autoindex 模塊先后看到當前請求的 URI,/index.html
和/hello.html
,并不以 /
結尾,于是直接棄權,將處理權轉給了最后運行的 ngx_static
模塊。ngx_static
模塊根據 root 指令指定的“文檔根目錄”(document root),分別將請求 URI /index.html
和 /hello.html
映射為文件系統路徑 /var/www/index.html
和 /var/www/hello.html
,在確認這兩個文件存在后,將它們的內容分別作為響應體輸出,并自動設置 Content-Type
、Content-Length
以及 Last-Modified
等響應頭。
為了確認 ngx_static
模塊確實運行了,可以啟用 (一) 中介紹過的 Nginx “調試日志”,然后再次請求 /index.html
這個接口。此時,在 Nginx 錯誤日志文件中可以看到類似下面這一行的調試信息:
[debug] 3033#0: *1 http static fd: 8
這一行信息便是 ngx_static
模塊生成的,其含義是“正在輸出的靜態文件的描述符是數字 8
”。當然,具體的文件描述符編號會經常發生變化,這里只是我機器的一次典型輸出。值得一提的是,能生成這一行調試信息的還有標準模塊 ngx_gzip_static ,但它默認是不啟用的,后面會專門介紹到這個模塊。
注意上面這個例子中使用的 root 配置指令只起到了聲明“文檔根目錄”的作用,并不是它開啟了ngx_static
模塊。ngx_static
模塊總是處于開啟狀態,但是否輪得到它運行就要看 content
階段先于它運行的那些模塊是否“棄權”了。為了進一步確認這一點,來看下面這個空白 location
的定義:
location / {
}
因為沒有配置 root 指令,所以在訪問這個接口時,Nginx 會自動計算出一個缺省的“文檔根目錄”。該缺省值是取所謂的“配置前綴”(configure prefix)路徑下的 html/
子目錄。舉一個例子,假設“配置前綴”是/foo/bah/
,則缺省的“文檔根目錄”便是 /foo/bar/html/
.
那么“配置前綴”是由什么來決定的呢?默認情況下,就是 Nginx 安裝時的根目錄(或者說 Nginx 構造時傳遞給 ./configure
腳本的 --prefix
選項的路徑值)。如果 Nginx 安裝到了 /usr/local/nginx/
下,則“配置前綴”便是 /usr/local/nginx/
,同時默認的“文檔根目錄”便是 /usr/local/nginx/html/
. 不過,我們也可以在啟動 Nginx 的時候,通過 --prefix
命令行選項臨時指定自己的“配置前綴”路徑。假設我們啟動 Nginx 時使用的命令是
nginx -p /home/agentzh/test/
則對于該服務器實例,其“配置前綴”便是 /home/agentzh/test/
,而默認的“文檔根目錄”便是/home/agentzh/test/html/
. “配置前綴”不僅會決定默認的“文檔根目錄”,還決定著 Nginx 配置文件中許多相對路徑值如何解釋為絕對路徑,后面我們還會看到許多需要引用到“配置前綴”的例子。
獲取當前“文檔根目錄”的路徑有一個非常簡便的方法,那就是請求一個肯定不存在的文件所對應的資源名,例如:
$ curl 'http://localhost:8080/blah-blah.txt'
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
我們會很自然地得到 404
錯誤頁。此時再看 Nginx 錯誤日志文件,應該會看到類似下面這一行錯誤消息:
[error] 9364#0: *1 open() "/home/agentzh/test/html/blah-blah.txt" failed (2: No such file or directory)
這條錯誤消息是 ngx_static
模塊打印出來的,因為它并不能在文件系統的對應路徑上找到名為 blah-blah.txt
的文件。因為這條錯誤信息中包含有 ngx_static
試圖打開的文件的絕對路徑,所以從這個路徑不難看出,當前的“文檔根目錄”是 /home/agentzh/test/html/
.
很多初學者會想當然地把 404
錯誤理解為某個 location
不存在,其實上面這個例子表明,即使 location
存在并成功匹配,也是可能返回 404
錯誤頁的。因為決定著 404
錯誤頁的是抽象的“資源”是否存在,而非某個具體的 location
是否存在。
初學者常犯的一個錯誤是忘記配置 content
階段的模塊指令,而他們自己其實并不期望使用 content
階段缺省運行的靜態資源服務,例如:
location /auth {
access_by_lua '
-- a lot of Lua code omitted here...
';
}
顯然,這個 /auth
接口只定義了 access
階段的配置指令,即 access_by_lua,并未定義任何 content
階段的配置指令。于是當我們請求 /auth
接口時,在 access
階段的 Lua 代碼會如期執行,然后 content
階段的那些靜態文件服務會緊接著自動發生作用,直至 ngx_static
模塊去文件系統上找名為 auth
的文件。而經常地,404
錯誤頁會拋出,除非運氣太好,在對應路徑上確實存在一個叫做 auth
的文件。所以,一條經驗是,當遇到意外的 404
錯誤并且又不涉及靜態文件服務時,應當首先檢查是否在對應的 location
配置塊中恰當地配置了 content
階段的模塊指令,例如 content_by_lua、 echo 以及 proxy_pass 之類。當然,Nginx 的error.log
文件一般總是會提供各種意外問題的答案,例如對于上面這個例子,我的 error.log
中有下面這條錯誤信息:
[error] 9364#0: *1 open() "/home/agentzh/test/html/auth" failed (2: No such file or directory)RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成