編譯自:
nginx-web-server
目錄:
- 建立虛擬服務器
- 配置 location
- 使用變量
- 返回指定的狀態碼
- URI 重寫
- 對 HTTP Response 進行重寫
- 處理錯誤
這里有一個視頻,有興趣可以看看:nginx 安裝與調優,其中講述了以下主題:
- nginx 怎樣使你的應用能更快響應、具有更好的伸縮性、更快、更安全?
- 如何安裝 nginx
- 為 nginx 調整操作系統參數
從高層次來說,配置 nginx 為一個 web 服務器,主要是定義:
- 對哪些 URL 進行處理
- 如何處理對于這些 URL 的 HTTP 請求
從較低層次來說,主要是定義一組 虛擬服務器 ,讓這些 虛擬服務器 去處理對于 特定域名 或 IP地址 的請求。
更多關于如何配置 nginx 的信息,可參考 Creating NGINX Plus Configuration Files。
每個 虛擬服務器 都定義了 location 區塊,location 區塊定義了對于指定的一組 URI 是如何進行處理的。
在 location 中可作如下定義:
- 轉發請求到后端服務器(也就是將此 nginx 服務器作為代理),或者返回一個文件;
- 對 URI 進行重寫,將請求重定向至其它的 location 或者其它虛擬服務器;
- 返回指定的 error code,并且可以為每個 error code 配置一個指定的 error page。
建立虛擬服務器
在 nginx 配置文件中,必須包含至少一個 server 指令,server 指令定義了一個虛擬服務器。當一個請求到來時,nginx 首先要決定選擇哪一個虛擬服務器處理該請求。
server 指令在 http context 中進行定義,例如:
http {
server {
# Server configuration
}
}
在 http context 中,可添加多個 server 指令以定義多個虛擬服務器。
在 server 區塊中,一般會包含一個 listen 指令。listen 指令是用于定義該虛擬服務器監聽于哪個 IP地址 和 端口(或者定義一個 Unix socket 文件及路徑)。IP地址可為 IPv4 或 IPv6 地址;IPv6 地址應放入方括號中。
以下是一個例子,該服務器監聽于 127.0.0.1 和 8080 端口:
server {
listen 127.0.0.1:8080;
# The rest of server configuration
}
如果不指定端口地址,將使用標準的端口地址,即 80 端口。如果不指定 IP 地址,將監聽于所有 IP 地址之上。如果 server 區塊中沒有包含 listen 指令,根據超級用戶的權限,將使用 標準的 80/tcp 端口或者 默認的 8000/tcp 端口。
如果同時有多個 虛擬主機 匹配了請求的IP地址和端口,nginx 將測試請求的 Host 首部字段,將它與 server 區塊中的 server_name 指令的配置進行對比。server_name 指令的值有三種:準確的主機名、通配主機名、正則表達式主機名。通配主機名在其起始、或末尾有一個 * 符號,或者起始和末尾都有一個 * 符號,* 可匹配任意長度的字符串;nginx 的正則表達式兼容 Perl 的正則語法,在正則表達式之前需有~
前綴。
以下是關于 準確的主機名 的例子:
server {
listen 80;
server_name example.org www.example.org;
...
}
如果有多個 server_name 指定的主機名與請求首部的 Host 字段匹配,nginx 按如下的優先次序選擇,當找到第一個能匹配的 server name 就終止查找:
- 準確的主機名
- 以 “*” 起始的最長的通配主機名,比如 *.example.org
- 以 “*” 結尾的最長的通配主機名,比如 mail.*
- 第一個匹配的正則表達式(按照配置文件中的順序)
如果沒有找到與 Host 字段匹配的 server name,nginx 將把請求路由到該端口地址的 默認的虛擬主機。 如果沒有特別指定,默認的虛擬主機 就是 nginx.conf 文件中的第一個定義的 虛擬主機。也可以使用 listen 指令的 default_server 參數顯式地指定一個虛擬主機為 默認的虛擬主機 :
server {
listen 80 default_server;
...
}
配置 location
nginx 可基于請求的 URI,將訪問流量轉發給不同的后端服務器,或者將不同的文件提供給客戶端。這個功能由 location 指令定義,location 指令在 server 區塊中定義。
舉個例子,你可以定義三個 location 區塊,將一些請求轉發給一個后端服務器,將另一些請求轉發給另一個不同的后端服務器,余下的請求從本地的文件系統中提供客戶端所請求的文件。
當一個請求交給了某個虛擬主機之后,nginx 將請求的 URI 與該虛擬主機內所有的 location 指令的定義進行對比,然后將請求映射到匹配的 location 之中。
在每個 location 區塊中,一般可以放置更多的 location 指令(有少數例外),用于進一步對請求進行分組處理。
Note: 在本文中,location 這個詞只表示一個單獨的 location 上下文。
location 指令可定義兩種參數:
- 前綴字符串(路徑名)
- 正則表達式
如果一個請求的 URI 以某個 前綴字符串 為起始,則與該 location 匹配。
下面舉一個 前綴字符串 的例子,在下面的例子中,該 location 的參數為 /some/path/ ,它能匹配類似于 URI 為 /some/path/document.html 的請求:
location /some/path/ {
...
}
正則表達式必須帶有前綴 ~
或 ~*
(忽略大小寫)。在下面的例子中,該 location 可匹配包含 .html 或者 .htm 的請求:
location ~ \.html? {
...
}
為了找到某個 URI 的最佳匹配,nginx 首先比較 URI 和 前綴字符串,然后比較 URI 和 正則表達式。
正則表達式擁有更高的優先級,除非正則表達式使用了 ^~ 修飾符。
在所有匹配的 前綴字符串 中,nginx 選擇其中最長及最完整的前綴字符串所對應的 location。
下面是準確的查找 loction 的過程:
- 將 URI 與所有的 前綴字符串 進行比較
- 修飾符 = 表示 URI 與 前綴字符串 必須精確匹配。如果能夠精確匹配,則結束查找
- 如果在匹配的最長的 前綴字符串 前添加了 ^~ 修飾符,則不會再檢查正則表達式
- 保存匹配的最長的 前綴字符串
- 測試 URI 和 正則表達式的匹配情況
- 一旦找到匹配的正則表達式,則終止查找,并使用對應的 location
- 如果沒有匹配的正則表達式,使用保存匹配的最長的 前綴字符串
如果對于 / 的請求很頻繁,可為 location / 添加 = 修飾符,這樣可以加速處理過程,因為一次匹配查找即可結束:
location = / {
...
}
在 location 區塊中,可匹配將請求轉發給后端服務器。在下面的例子中,對于匹配第二個 location 的請求,將被轉發給 www.example.com:
server {
location /images/ {
root /data;
}
location / {
proxy_pass http://www.example.com;
}
}
root 指令指定了提供靜態文件的本地路徑。如果請求的 URI 匹配第一個 location,將 URI 追加到 /data 之后即可得到文件的訪問路徑:/data/URI。比如請求的 URI 為 /images/example.png,nginx 將 /data/images/example.png 文件返回給客戶端。
proxy_pass 指令用于將請求轉發給后端服務器,參數值即為后端服務器的訪問路徑 URL。從后端服務器取回響應之后,nginx 再轉發給客戶端。在上面的例子中,所有不以 /images/ 起始的 URI,對應的請求將被轉發給后端服務器。
使用變量
在 nginx.conf 中可使用變量。變量的值在運行時進行計算,可作為指令的參數使用。引用變量,使用 $
符號,比如 $remote_addr
。變量的定義信息依賴于 nginx 當前的狀態,比如當前被處理的請求的屬性等。
有許多變量是預定義的,比如 core HTTP 定義的變量;另外你也可以自定義變量,使用 set,map,或 geo 指令進行自定義。
大多數變量的值是在運行時計算出來的,其中包含特定請求的信息。比如 $remote_addr
包含客戶端的 IP地址 信息,而 $uri
包含當前的 URI 值。
返回指定的狀態碼
在有些情況下,比如當一個頁面已經被臨時或永久地移動到其他位置,訪問這種 web 站點的 URI 時需要立即返回一個錯誤碼或者重定向碼。最容易的方法是使用 return 指令:
location /wrong/url {
return 404;
}
return 指令的第一個參數是響應狀態碼。第二參數是可選的,可以是一個重定向的 URL(for codes 301, 302, 303, and 307),或者是放入響應 body 的一段文本。比如:
location /permanently/moved/url {
return 301 http://www.example.com/moved/here;
}
return 指令可放入 location 和 server 上下文中。
URI 重寫
在處理請求的過程中,使用 rewrite 指令可對 URI 進行多次修改。
rewrite 指令的語法為:
rewrite regex replacement [flag]
regex 是用于匹配 URI 的正則表達式,replacement 參數用于替換匹配的 URI。flag 參數是可選的,可用于終止進一步的 rewrite 指令操作,或者發送重定向(301 或 302)給客戶端,例如:
location /users/ {
rewrite ^/users/(.*)$ /show?user=$1 break;
}
在 server 上下文和 location 上下文中,都可包含多個 rewrite 指令。nginx 按照 rewrite 指令出現的順序依次進行處理。當一個 server 上下文被選擇用于處理請求時,在 server 上下文中的 rewirte 指令被執行一次。
當 nginx 對一組 rewrite 指令進行處理之后,它根據新的 URI 選擇匹配的 location 上下文。如果在選擇的 location 中也包含 rewrite 指令,它們將被依次執行。如果 URI 匹配其中任意一個,當所有定義的 rewrite 指令被處理之后,開始對新的 URI 進行查找。
如下例子中,rewrite 指令和 return 指令聯合使用:
server {
...
rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 last;
rewrite ^(/download/.*)/audio/(.*)\..*$ $1/mp3/$2.ra last;
return 403;
...
}
上例中,對兩組 URI 進行了區分。
類似于 /download/some/media/file 的 URI 請求被替換為 /download/some/mp3/file.mp3。因為該 rewrite 指令添加了 last flag,它將跳過后面的指令(第二個 rewrite 指令和 return 指令)。然后 nginx 開始處理新的 URI。
類似于 /download/some/audio/file 的 URI 請求被替換為 /download/some/mp3/file.ra。因為該 rewrite 指令添加了 last flag,它將跳過后面的指令(return 指令)。
如果 URI 不匹配 location 中的任何一個 rewrite 指令,nginx 返回 403 狀態碼給客戶端。
rewrite 指令有兩個參數可以終止 rewrite 指令集的處理:
last - 停止當前 server 或 location 上下文中的 rewrite 指令集的執行,nginx 對修改后的 URI 進行匹配查找。如果新匹配的 location 中包含 rewrite 指令,URI 有可能被再次修改。
break - 如同 break 指令,終止當前上下文中的 rewrite 指令集的執行,nginx 對修改后的 URI 進行匹配查找,在新匹配的 location 中如果包含 rewrite 指令,它們不會被執行。
對 HTTP Response 進行重寫
有時你需要對 HTTP 響應報文的內容進行重寫或修改。你可以使用 sub_filter 指令來定義重寫操作。該指令支持變量和鏈式替換,可作復雜的修改:
舉個例子,你可以對指向一個服務器的絕對鏈接進行修改:
location / {
sub_filter /blog/ /blog-staging/;
sub_filter_once off;
}
另一個例子,修改 http:// 為 https:// ,并且將 localhost 地址修改為請求首部中的 host 字段的值。sub_filter_once 指令告訴 nginx 是否對所有匹配的字符串重復地執行替換操作,還是只在第一次遇到匹配的字符串時,執行一次替換操作(只替換一次)。sub_filter_once 默認為 on,表示只在第一次遇到匹配的字符串時,替換一次:
location / {
sub_filter 'href="https://$host/';
sub_filter 'img src="http://127.0.0.1:8080/' 'img src="https://$host/';
sub_filter_once on;
}
注意,如果響應報文的一部分已經被一個 sub_filter 指令所修改,當另一個 sub_filter 也匹配該部分時,不會再進行修改。
處理錯誤
使用 error_page 指令,你可以配置 nginx :
- 返回一個 error code 以及一個自定制的頁面;
- 在響應報文中,替換一個不同的 error code
- 發送重定向指令給瀏覽器,指向不同的 URI
在下面的例子中,error_page 指令指定了 /404.html 頁面,這個頁面在返回 404 code 時會一并返回給客戶端:
error_page 404 /404.html;
要注意的是,這個指令不是立即返回該錯誤給客戶端(這是 return 指令做的事),這只是定義了如果處理該錯誤。該 error code 可來自于一個后端服務器,或者在 nginx 做一些處理時出現(例如,當 nginx 不能找到請求的文件時,將返回 404 錯誤)。
下面的例子中,當 nginx 找不到請求的頁面時,它將 code 404 替換為了 code 301,并將返回一個重定向給客戶端:http:/example.com/new/path.html。
這個配置是很有用的,當客戶端嘗試以一個老舊的 URI 請求頁面時,可返回重定向指令,令其訪問在新路徑下的文件。
code 301 告訴瀏覽器,該頁面已經被永久地轉移到了其他地方,它需要將返回的新的地址替換原來的老的地址:
location /old/path.html {
error_page 404 =301 http:/example.com/new/path.html;
}
下面的例子中,當請求 /images/ 路徑下的文件發生文件未找到的錯誤時,通過內部重定向轉發到后端服務器。因為在 error_page 指令中,在 = 后面沒有定義替換的狀態碼,所以返回給客戶端的狀態碼由后端服務器指定(不一定是 404):
server {
...
location /images/ {
# Set the root directory to search for the file
root /data/www;
# Disable logging of errors related to file existence
open_file_cache_errors off;
# Make an internal redirect if the file is not found
error_page 404 = /fetch$uri;
}
location /fetch/ {
proxy_pass http://backend/;
}
}
error_page 指令告訴 nginx ,當發生 file not found 錯誤時,做一個內部的重定向。$uri 變量的值為當前請求的 URI,在重定向中被轉發給了后端服務器。
例如,如果 /images/some/file 未找到,它被替換為 /fetch/images/some/file,對這個新 URI,nginx 會查找與其匹配的 location,這里就是 location /fetch/,然后就被轉發給后端服務器了。
(可參考 反向代理)
當發送 file not found 錯誤時,open_file_cache_errors 指令在這里用于防止記錄錯誤日志。因為請求已經被轉發給后端,所以不應該記錄錯誤日志。
版權信息:
*本文編譯自 nginx.com