ginx的Rewrite規(guī)則與實例
Nginx Rewrite 規(guī)則相關(guān)指令
相關(guān)指令有if,rewrite,set,return,break等,其中最關(guān)鍵的就是rewrite.一個簡單的Nginx Rewrite規(guī)則語法如下:
rewrite ^/b/(.*)\.html /play.php?video=break;
1.break指令
默認值:none ;使用環(huán)境:server,location,if ;
該指令的作用是完成當(dāng)前的規(guī)則集,不再處理rewrite指令。
2.if指令
默認值:none ;使用環(huán)境:server,location
該指令用于檢查一個條件是否符合,如果條件符合,則執(zhí)行大括號內(nèi)的語句。If指令不支持嵌套,不支持多個條件&&和||處理。
A.變量名,錯誤的值包括:空字符串""或者任何以0開始的字符串
B.變量比較可以使用"="(表示等于)和"!="(表示不等于)
C.正則表達式模式匹配可以使用"~*"和"~"符號
D."~"符號表示區(qū)分大小寫字母的匹配
E."~*"符號表示不區(qū)分大小寫字母的匹配
F."!~"和"!~*"符號的作用剛好和"~"、"~*"相反,表示不匹配
G."-f"和"!-f"用來判斷文件是否存在
H."-d"和"!-d"用來判斷目錄是否存在
I."-e"和"!-e"用來判斷文件或目錄是否存在
J."-x"和"!-x"用來判斷文件是否為可執(zhí)行
K.部分正則表達式可以在()內(nèi),用~來訪問
3.return指令
語法:return code ;使用環(huán)境:server,location,if ;
該指令用于結(jié)束規(guī)則的執(zhí)行并返回狀態(tài)碼給客戶端。
示例:如果訪問的URL以".sh"或".bash"結(jié)尾,則返回403狀態(tài)碼
location ~ .*\.(sh|bash)?$
{
return 403;
}
Rewrite根據(jù)nginx提供的全局變量或自己設(shè)置的變量,結(jié)合正則表達式和標(biāo)志位實現(xiàn)url重寫和者重定向。
二、語法和參數(shù)說明
rewrite <regex> <replacement> <flag>;
關(guān)鍵字 正則表達式 代替的內(nèi)容 重寫類型
Rewrite:一般都是rewrite
Regex:可以是字符串或者正則來表示想要匹配的目標(biāo)URL
Replacement:將正則匹配的內(nèi)容替換成replacement
Flag:flag標(biāo)示,重寫類型:
- last:本條規(guī)則匹配完成后,繼續(xù)向下匹配新的location URI規(guī)則;相當(dāng)于Apache里德(L)標(biāo)記,表示完成rewrite,瀏覽器地址欄URL地址不變;一般寫在server和if中;
- break:本條規(guī)則匹配完成后,終止匹配,不再匹配后面的規(guī)則,瀏覽器地址欄URL地址不變;一般使用在location中;
- redirect:返回302臨時重定向,瀏覽器地址會顯示跳轉(zhuǎn)后的URL地址;
- permanent:返回301永久重定向,瀏覽器地址欄會顯示跳轉(zhuǎn)后的URL地址;
server {
# 訪問 /last.html 的時候,頁面內(nèi)容重寫到 /index.html 中,并繼續(xù)后面的匹配,瀏覽器地址欄URL地址不變
rewrite /last.html /index.html last;
# 訪問 /break.html 的時候,頁面內(nèi)容重寫到 /index.html 中,并停止后續(xù)的匹配,瀏覽器地址欄URL地址不變;
rewrite /break.html /index.html break;
# 訪問 /redirect.html 的時候,頁面直接302定向到 /index.html中,瀏覽器地址URL跳為index.html
rewrite /redirect.html /index.html redirect;
# 訪問 /permanent.html 的時候,頁面直接301定向到 /index.html中,瀏覽器地址URL跳為index.html
rewrite /permanent.html /index.html permanent;
# 把 /html/*.html=> /post/*.html ,301定向
rewrite ^/html/(.+?).html$ /post/$1.html permanent;
# 把 /search/key=> /search.html?keyword=key
rewrite ^/search\/([^\/]+?)(\/|$) /search.html?keyword=$1 permanent;
# 把當(dāng)前域名的請求,跳轉(zhuǎn)到新域名上,域名變化但路徑不變
rewrite ^/(.*) http://www.jd.com/$1 permanent;
}
if (表達式) {
}
#當(dāng)表達式只是一個變量時,如果值為空或任何以0開頭的字符串都會當(dāng)做false直接比較變量和內(nèi)容時,使用=或!=~正則表達式匹配,~*不區(qū)分大小寫的匹配,!~區(qū)分大小寫的不匹配
$args :這個變量等于請求行中的參數(shù),同$query_string
$content_length : 請求頭中的Content-length字段。
$content_type : 請求頭中的Content-Type字段。
$document_root : 當(dāng)前請求在root指令中指定的值。
$host : 請求主機頭字段,否則為服務(wù)器名稱。
$http_user_agent : 客戶端agent信息
$http_cookie : 客戶端cookie信息
$limit_rate : 這個變量可以限制連接速率。
$request_method : 客戶端請求的動作,通常為GET或POST。
$remote_addr : 客戶端的IP地址。
$remote_port : 客戶端的端口。
$remote_user : 已經(jīng)經(jīng)過Auth Basic Module驗證的用戶名。
$request_filename : 當(dāng)前請求的文件路徑,由root或alias指令與URI請求生成。
$scheme : HTTP方法(如http,https)。
$server_protocol : 請求使用的協(xié)議,通常是HTTP/1.0或HTTP/1.1。
$server_addr : 服務(wù)器地址,在完成一次系統(tǒng)調(diào)用后可以確定這個值。
$server_name : 服務(wù)器名稱。
$server_port : 請求到達服務(wù)器的端口號。
$request_uri : 包含請求參數(shù)的原始URI,不包含主機名,如:”/foo/bar.php?arg=baz”。
$uri : 不帶請求參數(shù)的當(dāng)前URI,$uri不包含主機名,如”/foo/bar.html”。
$document_uri : 與$uri相同。
例子:
URL:http://localhost:81/download/stat.php?id=1585378&web_id=1585378
Server_Dir:/var/www/html
$host:localhost
$server_port:81
$request_uri:/download/stat.php?id=1585378&web_id=1585378
$document_uri:/download/stat.php
$document_root:/var/www/html
$request_filename:/var/www/html/download/stat.php
# 如果文件不存在則返回400
if (!-f $request_filename) {
return 400;
}
# 如果host是www.360buy.com,則301到www.jd.com中
if ( $host !="www.jd.com" ){
rewrite ^/(.*)$ https://www.jd.com/$1 permanent;
}
# 如果請求類型是POST則返回405,return不能返回301,302
if ($request_method=POST) {
return 405;
}
# 如果參數(shù)中有 a=1 則301到指定域名
if ($args ~ a=1) {
rewrite ^ http://example.com/ permanent;
}
- 文件名及參數(shù)重寫
location=/index.html {
# 修改默認值為
set $name test;
# 如果參數(shù)中有 name=xx 則使用該值
if ($args ~* name=(\w+?)(&|$)) {
set $name $1;
}
# permanent 301重定向
rewrite ^ /$name.html permanent;
}
- 隱藏真實目錄
server {
root /var/www/html;
# 用 /html_test 來掩飾 html
location / {
# 使用break拿一旦匹配成功則忽略后續(xù)location
rewrite /html_test /html break;
}
# 訪問真實地址直接報沒權(quán)限
location /html {
return 403;
}
}
- 禁止指定IP訪問
location / {
if ($remote_addr=192.168.1.253) {
return 403;
}
}
- 如果請求的文件不存在,則反向代理到localhost 。這里的break也是停止繼續(xù)rewrite
if (!-f $request_filename){
break;
proxy_pass http://127.0.0.1;
}
- 對/images/bla_500x400.jpg文件請求,重寫到/resizer/bla.jpg?width=500&height=400地址,并會繼續(xù)嘗試匹配location。
rewrite ^/images/(.*)_(\d+)x(\d+)\.(png|jpg|gif)$ /resizer/$1.$4?width=$2&height=$3? last;
Proxy_pass反向代理,用的是nginx的Proxy模塊。
第一種:
location /proxy/ {
proxy_pass http://127.0.0.1/;
}
代理到URL:http://127.0.0.1/test.html
第二種:
location /proxy/ {
proxy_pass http://127.0.0.1; #少/
}
代理到URL:http://127.0.0.1/proxy/test.html
第三種:
location /proxy/ {
proxy_pass http://127.0.0.1/aaa/;
}
代理到URL:http://127.0.0.1/aaa/test.html
第四種(相對于第三種,最后少一個 / )
location /proxy/ {
proxy_pass http://127.0.0.1/aaa;
}
代理到URL:http://127.0.0.1/aaatest.html
- proxy_set_header Host $host; 作用web服務(wù)器上有多個站點時,用該參數(shù)header來區(qū)分反向代理哪個域名。比如下邊的代碼舉例。
- proxy_set_header X-Forwarded-For $remote_addr; 作用是后端服務(wù)器上的程序獲取訪客真實IP,從該header頭獲取。部分程序需要該功能。
- Proxy_pass配合upstream實現(xiàn)負載均衡
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
upstream core_tomcat {
server 192.168.1.253:80 weight=5 max_fails=3 fail_timeout=30;
server 192.168.1.252:80 weight=1 max_fails=3 fail_timeout=30;
server 192.168.1.251:80 backup;
}
server {
listen 80;
server_name www.jd.com;
location /web {
proxy_pass http://core_tomcat;
proxy_set_header Host $host;
}
}
}
Nginx負載均衡的幾種模式
upstream core_tomcat {
server 192.168.1.253:80 max_fails=3 fail_timeout=30;
server 192.168.1.252:80 max_fails=3 fail_timeout=30;
}
upstream core_tomcat {
server 192.168.1.253:80 weight=2 max_fails=3 fail_timeout=30;
server 192.168.1.252:80 weight=8 max_fails=3 fail_timeout=30;
}
#假如有十個請求,八個會指向第二臺服務(wù)器,兩個指向第一臺;
upstream core_tomcat {
ip_hash;
server 192.168.1.253:80 max_fails=3 fail_timeout=30;
server 192.168.1.252:80 max_fails=3 fail_timeout=30;
}
upstream core_tomcat {
fair;
server 192.168.1.253:80 max_fails=3 fail_timeout=30;
server 192.168.1.252:80 max_fails=3 fail_timeout=30;
}
Nginx的rewrite模塊即ngx_http_rewrite_module標(biāo)準(zhǔn)模塊,主要功能是重寫請求URI,也是Nginx默認安裝的模塊。rewrite模塊會根據(jù)PCRE正則匹配重寫URI,然后根據(jù)指令參數(shù)或者發(fā)起內(nèi)部跳轉(zhuǎn)再一次進行l(wèi)ocation匹配,或者直接進行30x重定向返回客戶端。
rewrite模塊的指令就是一門微型的編程語言,包含set、rewrite、break、if、return等一系列指令。
set指令是由ngx_http_rewrite_module標(biāo)準(zhǔn)模塊提供的,用于向變量存放值。在Nginx配置文件中,變量只能存放一種類型的值,因為只存在一種類型的值,那就是字符串。
set指令的配置項格式如下:
set $variable value;
注意:在Nginx配置文件中,變量定義和使用都要以$開頭。Nginx變量名前面有一個$符號,這是記法上的要求。所有的Nginx變量在引用時必須帶上$前綴。另外,Nginx變量不能與Nginx服務(wù)器預(yù)設(shè)的全局變量同名。比如,我們的nginx.conf文件中有下面這一行配置:
set $a "hello world";
上面的語句中,set配置指令對變量$a進行了賦值操作,把字符串hello world賦給了它。也可以直接把變量嵌入字符串常量中以構(gòu)造出新的字符串:
set $a "foo";
set $b "$a, $a";
這個例子通過前面定義的變量$a的值來構(gòu)造變量$b的值,于是這兩條指令順序執(zhí)行完之后,$a的值是"foo",而$b的值則是"foo,foo"。把變量嵌入字符串常量中以構(gòu)造出新的字符串,這種技術(shù)在Linux Shell腳本中常常用到,并且被稱為“變量插值”(VariableInterpolation)。
set指令不僅有賦值的功能,還有創(chuàng)建Nginx變量的副作用,即當(dāng)作為賦值對象的變量尚不存在時,它會自動創(chuàng)建該變量。比如在上面這個例子中,若$a這個變量尚未創(chuàng)建,則set指令會自動創(chuàng)建$a這個用戶變量。
Nginx變量一旦創(chuàng)建,其變量名的可見范圍就是整個Nginx配置,甚至可以跨越不同虛擬主機的server配置塊。但是,對于每個請求,所有變量都有一份獨立的副本,或者說都有各變量用來存放值的容器的獨立副本,彼此互不干擾。Nginx變量的生命期是不可能跨越請求邊界的。
rewrite指令是由ngx_http_rewrite_module標(biāo)準(zhǔn)模塊提供的,主要功能是改寫請求URI。rewrite指令的格式如下:
rewrite regrex replacement [flag];
如果regrex匹配URI,URI就會被替換成replacement的計算結(jié)果,replacement一般是一個“變量插值”表達式,其計算之后的字符串就是新的URI。
下面的例子有兩個重新配置項,具體如下:
location /download/ {
rewrite ^/download/(.*)/video/(.*)$ /view/$1/mp3/$2.mp3 last;
rewrite ^/download/(.*)/audio/(.*)*$ /view/$1/mp3/$2.rmvb last;
return 404;
}
location /view {
echo "uri: $uri ";
}
在瀏覽器中請求http://crazydemo.com/download/1/video/10,地址發(fā)生了重寫,并且發(fā)生了location的跳轉(zhuǎn),結(jié)果如圖7-17所示。
圖7-17 輸出結(jié)果
在這個演示例子中,replacement中的占位變量、的值是指令參數(shù)regrex正則表達式從原始URI中匹配出來的子字符串,也叫正則捕獲組,編號從1開始。
rewrite指令可以使用的上下文為:server、location、if inlocation。
如果rewrite同一個上下文中有多個這樣的rewrite重新指令,匹配就會依照rewrite指令出現(xiàn)的順序先后依次進行下去,匹配成功之后并不會終止,而是繼續(xù)往下匹配,直到返回最后一個匹配的為止。如果想要中途中止,不再繼續(xù)往下匹配,可以使用第3個指令參數(shù)flag。flag參數(shù)的值有l(wèi)ast、break、redirect、permanent。
如果flag參數(shù)使用last值,并且匹配成功,那么停止處理任何rewrite相關(guān)的指令,立即用計算后的新URI開始下一輪的location匹配和跳轉(zhuǎn)。前面的例子使用的就是last參數(shù)值。
如果flag參數(shù)使用break值,就如同break指令的字面意思一樣,停止處理任何rewrite的相關(guān)指令,但是不進行l(wèi)ocation跳轉(zhuǎn)。
將上面的rewrite例子中的last參數(shù)值改成break,代碼如下:
location /view {
echo " view : $uri ";
}
location /download_break/ {
rewrite ^/download_break/(.*)/video/(.*)$ /view/$1/mp3/$2.mp3 break;
rewrite ^/download_break/(.*)/audio/(.*)*$ /view/$1/mp3/$2.rmvb break;
echo " download_break new uri : $uri ";
}
在瀏覽器中請求http://crazydemo.com/download_break/1/video/10,地址發(fā)生了重寫,但是location并沒有跳轉(zhuǎn),而是直接結(jié)束了,結(jié)果如圖7-18所示。
圖7-18 顯示結(jié)果
在location上下文中,last和break是有區(qū)別的:last其實就相當(dāng)于一個新的URL,Nginx進行了一次新的location匹配,通過last獲得一個可以轉(zhuǎn)到其他location配置中處理的機會(內(nèi)部的重定向);而break在一個location中將原來的URL(包括URI和args)改寫之后,再繼續(xù)進行后面的處理,這個重寫之后的請求始終都是在同一個location上下文中,并沒有發(fā)生內(nèi)部跳轉(zhuǎn)。
這里要注意:last和break的區(qū)別僅僅發(fā)生在location上下文中;如果發(fā)生在server上下文,那么last和break的作用是一樣的。
還要注意:在location上下文中的rewrite指令使用last指令參數(shù)會再次以新的URI重新發(fā)起內(nèi)部重定向,再次進行l(wèi)ocation匹配,而新的URI極有可能和舊的URI一樣再次匹配到相同的目標(biāo)location中,這樣死循環(huán)就發(fā)生了。當(dāng)循環(huán)到第10次時,Nginx會終止這樣無意義的循環(huán)并返回500錯誤。這一點需要特別注意。
如果rewrite指令使用的flag參數(shù)的值是permanent,就表示進行外部重定向,也就是在客戶端進行重定向。此時,服務(wù)器將新URI地址返回給客戶端瀏覽器,并且返回301(永久重定向的響應(yīng)碼)給客戶端。客戶端將使用新的重定向地址再發(fā)起一次遠程請求。
永久重定向permanent的使用示例如下:
#rewrite指令permanent參數(shù)演示
location /download_permanent/ {
rewrite ^/download_permanent/(.*)/video/(.*)$ /view/$1/mp3/$2.mp3 permanent;
rewrite ^/download_permanent/(.*)/audio/(.*)*$ /view/$1/mp3/$2.rmvb permanent; return 404;
}
在瀏覽器中請求http://crazydemo.com/download_permanent/1/video/10,輸出的結(jié)果如圖7-19所示。
圖7-19 輸出的結(jié)果
從以上結(jié)果可以看出,永久重定向有兩個比較大的特點:
(1)瀏覽器的地址欄地址變成了重定向地址
http://crazydemo.com/view/1/mp3/10.mp3。
(2)從Fiddler抓包工具可以看到,第一個請求地址的響應(yīng)狀態(tài)碼為301,如圖7-20所示。
圖7-20 永久重定向的響應(yīng)碼示意圖
外部重定向與內(nèi)部重定向是有本質(zhì)區(qū)別的。從數(shù)量上說,外部重定向有兩次請求,內(nèi)部重定向只有一次請求。通過上面的幾個示例,大家應(yīng)該體會得相當(dāng)深刻了。
如果rewrite指令使用的flag參數(shù)的值是redirect,就表示進行外部重定向,表現(xiàn)的行為與permanent參數(shù)值完全一樣,不同的是返回302(臨時重定向的響應(yīng)碼)給客戶端。
有關(guān)redirect參數(shù)值的實例這里不進行演示,大家可自行下載和運行本文的源碼并細細體會。
rewrite能夠利用正則捕獲組設(shè)置變量,作為實驗,我們可以在Nginx的配置文件中加入這么一條location規(guī)則:
location /capture_demo {
rewrite ^/capture_demo/(.*)/video/(.*)$ /view/$1/mp3/$2.mp3 break;
rewrite ^/capture_demo/(.*)/audio/(.*)*$ /view/$1/mp3/$2.rmvb break;
捕獲組
捕獲組 echo " 捕獲組1:$1;捕獲組2:$2";
}
在瀏覽器中請求http://crazydemo.com/capture_demo/group1/video/group2,輸出的結(jié)果如圖7-21所示。
圖7-21 輸出的結(jié)果
if條件指令配置項的格式如下:
if (condition) {...}
當(dāng)if條件滿足時,執(zhí)行配置塊中的配置指令。if的配置塊相當(dāng)于引入了一個新的上下文作用域。if條件指令適用于server和location兩個上下文。
condition條件表達式可以用到一系列比較操作符,大致如下:
(1)==:相等。
(2)!=:不相等。
(3)~:區(qū)分字母大小寫模式匹配。
(4)~*:不區(qū)分字母大小寫模式匹配。
(5)還有其他幾個專用比較符號,比如判斷文件及目錄是否存在的符號,等等。
下面是一個簡單的演示程序,根據(jù)內(nèi)置變量$http_user_agent的值判斷客戶端的類型,代碼如下:
#if指令的演示程序
location /if_demo {
if ($http_user_agent ~*"Firefox") { #匹配Firefox瀏覽器
return 403;
}
匹配谷歌瀏覽器
if ($http_user_agent ~*"Chrome") { #匹配Chrome谷歌瀏覽器
return 301;
}
if ($http_user_agent ~*"iphone") { #匹配iPhone手機
return 302;
}
if ($http_user_agent ~*"android") { #匹配安卓手機
return 404;
}
return 405; #其他瀏覽器默認訪問規(guī)則
}
在火狐瀏覽器中訪問http://crazydemo.com/if_demo,結(jié)果如圖7-22所示。
圖7-22 火狐瀏覽器的訪問結(jié)果
在谷歌瀏覽器中訪問http://crazydemo.com/if_demo,結(jié)果如圖7-23所示。
圖7-23 谷歌瀏覽器的訪問結(jié)果
在演示代碼中使用到了return指令,用于返回HTTP的狀態(tài)碼。
return指令會停止同一個作用域的剩余指令處理,并返回給客戶端指定的響應(yīng)碼。
return指令可以用于server、location、if上下文中,執(zhí)行階段是rewrite階段。其指令的格式如下:
#格式一:返回響應(yīng)的狀態(tài)碼和提示文字,提示文字可選
return code [text];
#格式二:返回響應(yīng)的重定向狀態(tài)碼(如301)和重定向URL
return code URL;
#格式三:返回響應(yīng)的重定向URL,默認的返回狀態(tài)碼是臨時重定向302
return URL;
response header一般是以key:value的形式,例如Content-Encoding:
gzip、Cache-Control:no-store,設(shè)置的命令如下:
add_header Cache-Control no-store
add_header Content-Encoding gzip
但是,有一個十分常用的response header為Content-Type,可以在它設(shè)置了類型的同時指定charset,例如text/html;charset=utf-8,由于其存在分號,而分號在配置文件中作為結(jié)束符,因此在配置時需要用引號把其引起來,配置如下:
add_header Content-Type 'text/html; charset=utf-8';
另外,由于沒有單獨設(shè)置charset的key,因此要設(shè)置響應(yīng)的charset就需要使用Content-Type來指定charset。
使用AJAX進行跨域請求時,瀏覽器會向跨域資源的服務(wù)端發(fā)送一個OPTIONS請求,用于判斷實際請求是否安全或者判斷服務(wù)端是否允許跨域訪問,這種請求也叫作預(yù)檢請求。跨域訪問的預(yù)檢請求是瀏覽器自動發(fā)出的,用戶程序往往不知情,如果不進行特別的配置,那么客戶端發(fā)出一次請求,在服務(wù)端往往會收到兩個請求;一個是預(yù)檢請求;另一個是正式的請求。后端的服務(wù)器(PHP或者Tomcat)如果不經(jīng)過特殊的過濾,那么很容易將OPTIONS預(yù)檢請求當(dāng)成正式的數(shù)據(jù)請求。
對于客戶端而言,只有預(yù)檢請求返回成功,客戶端才開始正式請求。在實際的使用場景中,預(yù)檢請求比較影響性能,用戶往往會有兩倍請求的感覺,所以一般會在Nginx代理服務(wù)端對預(yù)檢請求進行提前攔截,同時對預(yù)檢請求設(shè)置比較長時間的有效期。
upstream zuul {
#server 192.168.233.1:7799;
server "192.168.233.128:7799";
keepalive 1000;
}
server {
listen 80;
server_name nginx.server *.nginx.server;
default_type 'text/html';
charset utf-8;
#轉(zhuǎn)發(fā)到上游服務(wù)器,但是 'OPTIONS' 請求直接返回空
location / {
if ($request_method='OPTIONS') {
add_header Access-Control-Max-Age 1728000;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'Keep-Alive,User-Agent,X-Requested-With,\
If-Modified-Since,Cache-Control,Content-Type,token';
return 204;
}
proxy_pass http://zuul/ ;
}
}
配置Nginx,加入Access-Control-Max-Age請求頭,用來指定本次預(yù)檢請求的有效期,單位為秒。上面結(jié)果中的有效期是20天(1 728 000秒),即允許緩存該條回應(yīng)1 728 000秒,在此期間客戶端不用發(fā)出另一條預(yù)檢請求。
大多數(shù)Nginx新手都會頻繁遇到這樣一個困惑:當(dāng)同一個location配置塊使用了多個Nginx模塊的配置指令時,這些指令的執(zhí)行順序很可能會跟它們的書寫順序大相徑庭。現(xiàn)在就來看這樣一個令人困惑的例子:
location /sequence_demo_1 {
set $a foo;
echo $a;
set $a bar;
echo $a;
}
上面的代碼先給變量$a賦值foo,隨后輸出,再給變量$a賦值bar,隨后輸出。如果這是一段Java代碼,毫無疑問,最終的輸出結(jié)果一定為“foo bar”。然而不幸的是,事實并非如此,在瀏覽器中訪問http://crazydemo.com/sequence_demo_1,結(jié)果如圖7-24所示。
圖7-24 輸出的結(jié)果
為什么出現(xiàn)了這種不合常理的現(xiàn)象呢?
前面講到,Nginx的請求處理階段共有11個,分別是post-read、server-rewrite、find-config、rewrite、post-rewrite、preaccess、access、post-access、try-files、content以及l(fā)og。其中3個比較常見的按照執(zhí)行時的先后順序依次是rewrite階段、access階段以及content階段。
Nginx的配置指令一般只會注冊并運行在其中的某一個處理階段,比如set指令就是在rewrite階段運行的,而echo指令只會在content階段運行。在一次請求處理流程中,rewrite階段總是在content階段之前執(zhí)行。因此,屬于rewrite階段的配置指令(示例中的set)總是會無條件地在content階段的配置指令(示例中的echo)之前執(zhí)行,即便是echo配置項出現(xiàn)在set配置項的前面。
上面例子中的指令按照請求處理階段的先后次序排序,實際的執(zhí)行次序如下:
location /sequence_demo_1 {
#rewrite階段的配置指令,執(zhí)行在前面
set $a foo;
set $a bar;
#content階段的配置指令,執(zhí)行在后面
echo $a;
echo $a;
}
所以,輸出的結(jié)果就是bar bar了。
*請認真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。