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