標籤:
通過Rewrite規則可以實現規範的URL、根據變數來做URL轉向及選擇配置,用好Rewrite有時起到事半功倍的效果。
文法
Nginx的Rewrite相比Apache的要好理解很多,主要使用指令有if、rewrite、set、return、break等,其中rewrite是最關鍵的指令。
- rewrite
文法: rewrite regex replacement [flag];
預設值:—
上下文:server, location, if
如果指定的Regex能匹配URI,此URI將被replacement參數定義的字串改寫。rewrite指令按其在設定檔中出現的順序執行。flag可以終止後續指令的執行。如果replacement的字串以“http://”或“https://”開頭,nginx將結束執行過程,並返回給用戶端一個重新導向。
可選的flag參數可以是其中之一:
- last
停止執行當前這一輪的ngx_http_rewrite_module指令集,然後尋找匹配改變後URI的新location;
- break
停止執行當前這一輪的ngx_http_rewrite_module指令集;
- redirect
在replacement字串未以“http://”或“https://”開頭時,使用返回狀態代碼為302的臨時重新導向;
- permanent
返回狀態代碼為301的永久重新導向。
舉例
server { ... rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 last; rewrite ^(/download/.*)/audio/(.*)\..*$ $1/mp3/$2.ra last; return 403; ...}但是當上述指令寫在“/download/”的location中時,應使用標誌break代替last,否則nginx會重複10輪迴圈,然後返回錯誤500(break和last的區別請參考break):
location /download/ { rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 break; rewrite ^(/download/.*)/audio/(.*)\..*$ $1/mp3/$2.ra break; return 403;}如果replacement字串包括新的請求參數,以往的請求參數會添加到新參數後面。如果不希望這樣,在replacement字串末尾加一個問號“?”,就可以避免,比如:
rewrite ^/users/(.*)$ /show?user=$1? last;
如果Regex中包含字元“}”或者“;”,整個運算式應該被包含在單引號或雙引號的引用中。
- break
文法: break;
預設值:—
上下文:server, location, if
停止處理當前這一輪的ngx_http_rewrite_module指令集。如:
if ($slow) { limit_rate 10k; break;}last和break標記的實現功能類似,但二者之間細微的差別,使用alias指令時必須用last標記,使用proxy_pass指令時要使用break標記。last標記在本條rewrite規則執行完畢後,會對其所在的server{...}標籤重新發起請求,而break標記則在本條規則匹配完成後終止匹配,不在匹配後邊的規則。因此,一般在根location中(即location / {...})或直接在server標籤中編寫rewrite規則,推薦使用last標記,在非location中(如location /cms/ {...}),則使用break標記。
對花括弧({和})來說,他們既能用在重新導向的Regex裡,也能用在設定檔裡分割代碼塊,為了避免衝突,Regex裡如果帶花括弧,應該使用雙引號(或單引號)包圍,如:把/photos/123456重新導向到:/path/to/photos/12/1234/123456.png
rewrite "/photos/([0-9]{2})([0-9]{2})([0-9]{2})" /path/to/photos/$1$2/$1$2$3.png>
- if
文法: if (condition) { ... }
預設值:—
上下文:server, location
用於檢查condition,如果為真,執行定義在大括弧中的rewrite模組指令。if指令不支援嵌套,不支援多個條件&&和||處理。
條件可以是下列任意一種:
- 變數名;如果變數值為空白或者是以“0”開始的字串,則條件為假;
- 使用“=”和“!=”運算子比較變數和字串;
- 使用“~”(大小寫敏感)和“~*”(大小寫不敏感)運算子匹配變數和Regex。Regex可以包含匹配組,匹配結果後續可以使用變數$1..$9引用。如果Regex中包含字元“}”或者“;”,整個運算式應該被包含在單引號或雙引號的引用中。
- 使用“-f”和“!-f”運算子檢查檔案是否存在;
- 使用“-d”和“!-d”運算子檢查目錄是否存在;
- 使用“-e”和“!-e”運算子檢查檔案、目錄或符號連結是否存在;
- 使用“-x”和“!-x”運算子檢查可執行檔;
如:
if ($http_user_agent ~ MSIE) { rewrite ^(.*)$ /msie/$1 break;}if ($http_cookie ~* "id=([^;]+)(?:;|$)") { set $id $1;}if ($request_method = POST) { return 405;}if ($slow) { limit_rate 10k;}if ($invalid_referer) { return 403;}
- set
文法: set variable value;
預設值:—
上下文:server, location, if
為指定變數variable設定變數值value。value可以包含文本、變數或者它們的組合,舉例參考下文。
- rewrite_log
文法:rewrite_log on | off;
預設值:
rewrite_log off;
上下文:http, server, location, if
開啟或者關閉將ngx_http_rewrite_module模組指令的處理日誌以notice層級記錄到錯誤記錄檔中。
- uninitialized_variable_warn
文法:uninitialized_variable_warn on | off;
預設值:
uninitialized_variable_warn on;
上下文:http, server, location, if
控制是否記錄變數未初始化的警告到日誌。
- return
文法:return code [text];
return code URL;
return URL;
預設值:—
上下文:server, location, if
停止處理並返回指定code給用戶端。狀態代碼可以使用這些值:204、400、402-406、408、410、411、413、416及500-504,此外,非標準狀態代碼444將以不發送任何Header頭的方式結束連結。
nginx用到的全域變數
$arg_PARAMETER #這個變數包含GET請求中,如果有變數PARAMETER時的值。$args #這個變數等於請求行中(GET請求)的參數,例如foo=123&bar=blahblah;$binary_remote_addr #二進位的客戶地址。$body_bytes_sent #響應時送出的body位元組數數量。即使串連中斷,這個資料也是精確的。$content_length #要求標頭中的Content-length欄位。$content_type #要求標頭中的Content-Type欄位。$cookie_COOKIE #cookie COOKIE變數的值$document_root #當前請求在root指令中指定的值。$document_uri #與$uri相同。$host #請求主機頭欄位,否則為伺服器名稱。$hostname #Set to the machine’s hostname as returned by gethostname$http_HEADER$is_args #如果有$args參數,這個變數等於”?”,否則等於”",空值。$http_user_agent #用戶端agent資訊$http_cookie #用戶端cookie資訊$limit_rate #這個變數可以限制串連速率。$query_string #與$args相同。$request_body_file #用戶端請求主體資訊的臨時檔案名稱。$request_method #用戶端請求的動作,通常為GET或POST。$remote_addr #用戶端的IP地址。$remote_port #用戶端的連接埠。$remote_user #已經經過Auth Basic Module驗證的使用者名稱。$request_completion #如果請求結束,設定為OK. 當請求未結束或如果該請求不是請求鏈串的最後一個時,為空白(Empty)。$request_method #GET或POST$request_filename #當前請求的檔案路徑,由root或alias指令與URI請求產生。$request_uri #包含請求參數的原始URI,不包含主機名稱,如:”/foo/bar.php?arg=baz”。不能修改。$scheme #HTTP方法(如http,https)。$server_protocol #請求使用的協議,通常是HTTP/1.0或HTTP/1.1。$server_addr #伺服器位址,在完成一次系統調用後可以確定這個值。$server_name #伺服器名稱。$server_port #請求到達伺服器的連接埠號碼。$uri #不帶請求參數的當前URI,$uri不包含主機名稱,如”/foo/bar.html”。該值有可能和$request_uri 不一致。$request_uri是瀏覽器發過來的值。該值是rewrite後的值。例如做了internal redirects後。
Rewrite規則執行個體
- 當訪問的檔案和目錄不存在時,重新導向到某個php檔案
if( !-e $request_filename ) {rewrite ^/(.*)$ index.php last;}
- 目錄對換 /123456/xxxx ====> /xxxx?id=123456
rewrite ^/(\d+)/(.+)/ /$2?id=$1 last;
- 如果用戶端使用的是IE瀏覽器,則重新導向到/ie目錄下
if( $http_user_agent ~ MSIE) {rewrite ^(.*)$ /ie/$1 break;}
- 禁止訪問多個目錄
location ~ ^/(cron|templates)/ {deny all;break;}
- 禁止訪問以/data開頭的檔案
location ~ ^/data {deny all;}
- 禁止訪問以.sh,.flv,.mp3為檔案尾碼名的檔案
location ~ .*\.(sh|flv|mp3)$ {return 403;}
- 設定某些類型檔案的瀏覽器緩衝時間
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {expires 30d;}location ~ .*\.(js|css)$ {expires 1h;}
Nginx和Apache的Rewrite規則執行個體對比
- 一般簡單的Nginx和Apache規則的區別不大,基本能夠完全相容,例如:
#ApacheRewriteRule ^/abc/$ /web/abc.php [L] #Nginxrewrite ^/abc/$ /web/abc.php last ;
我們可以看出來只要把Apache的RewriteRule改為Nginx的rewrite,Apache的[L]改為last 即可。
如果將Apache的規則改為Nginx規則後,用命令Nginx -t 檢查發現錯誤,則我們可以嘗試給條件加上引號,例如:
rewrite "^/([0-9]{5}).html$" /x.php?id=$1 last;
- Apache和Nginx的Rewrite規則在URL跳轉時有細微區別:
#ApacheRewriteRule ^/html/([a-zA-Z]+)/.*$ /$1/ [R=301,L]#Nginxrewrite ^/html/([a-zA-Z]+)/.*$ http://$host/$1/ premanent;
我們可以看到在Nginx的跳轉中,我們需要加上http://$host,這是在Nginx中強烈要求的。
- 下面是一些Apache和Nginx規則的對應關係
Apache的RewriteCond對應Nginx的if
Apache的RewriteRule對應Nginx的rewrite
Apache的[R]對應Nginx的redirect
Apache的[P]對應Nginx的last
Apache的[R,L]對應Nginx的redirect
Apache的[P,L]對應Nginx的last
Apache的[PT,L]對應Nginx的last
例如:允許指定的網域名稱訪問本站,其他的網域名稱一律轉向www.xiaozhe.comApache:
RewriteCond %{HTTP_HOST} !^(.*?)\.aaa\.com$ [NC]RewriteCond %{HTTP_HOST} !^localhost$ RewriteCond %{HTTP_HOST} !^192\.168\.0\.(.*?)$RewriteRule ^/(.*)$ http://www.xiaozhe.com [R,L]Nginx:
if( $host ~* ^(.*)\.aaa\.com$ ){set $allowHost ‘1’;}if( $host ~* ^localhost ){set $allowHost ‘1’;}if( $host ~* ^192\.168\.1\.(.*?)$ ){set $allowHost ‘1’;}if( $allowHost !~ ‘1’ ){rewrite ^/(.*)$ http://www.xiaozhe.com redirect ;}
Nginx的Rewrite規則與執行個體