Nginx 重寫規則指南
當運維遇到要重寫情況時,往往是要程序員把重寫規則寫好后,發給你,你再到生產環境下配置。對于重寫規則說到底就是正則匹配,做運維的豈能對正則表達式不了解的?最起碼最基本的正則表達式會寫。套用一句阿里的話(某網友說是阿里說的,不清楚到底是不是出自阿里)“不懂程序的運維,不是好運維;不懂運維的開發,不是好開發。”。 正則表達式也是一門語言哈。當你學習一門語言時,必然會遇到該門語言的正則表達式這章節的。 在這里推薦一本非常好的正則表達式書,包含常用的語言的正則寫法如sed、perl、bash、awk、php、c#、java、javascript、python、ruby等等,《Regular Expressions Cookbook, 2nd Edition》,也有中文版的,大家可以到網絡上找找。
本文介紹nginx的重寫模塊,創建重寫規則向導,便于快捷正確的創建新的重寫規則,不求救于人。同時,如果想把apache轉換成nginx,重寫規則也是要改的咯。
一. rewrite模塊介紹
nginx的重寫模塊是一個簡單的正則表達式匹配與一個虛擬堆疊機結合。依賴于PCRE庫,因此需要安裝pcre。根據相關變量重定向和選擇不同的配置,從一個location跳轉到另一個location,不過這樣的循環最多可以執行10次,超過后nginx將返回500錯誤。同時,重寫模塊包含set指令,來創建新的變量并設其值,這在有些情景下非常有用的,如記錄條件標識、傳遞參數到其他location、記錄做了什么等等。
二. rewrite模塊指令
break
語法:break
默認值:none
使用字段:server, location, if
完成當前設置的重寫規則,停止執行其他的重寫規則。
if
語法:if (condition) { … }
默認值:none
使用字段:server, location
注意:盡量考慮使用try_files代替。判斷的條件可以有以下值:
- 一個變量的名稱:空字符傳“”或者一些“0”開始的字符串為false。
- 字符串比較:使用=或!=運算符
- 正則表達式匹配:使用~(區分大小寫)和~*(不區分大小寫),取反運算!~和!~*。
- 文件是否存在:使用-f和!-f操作符
- 目錄是否存在:使用-d和!-d操作符
- 文件、目錄、符號鏈接是否存在:使用-e和!-e操作符
- 文件是否可執行:使用-x和!-x操作符
return
語法:return code
默認值:none
使用字段:server, location, if
停止處理并為客戶端返回狀態碼。非標準的444狀態碼將關閉連接,不發送任何響應頭。可以使用的狀態碼有:204,400,402-406,408,410, 411, 413, 416與500-504。如果狀態碼附帶文字段落,該文本將被放置在響應主體。相反,如果狀態碼后面是一個URL,該URL將成為location頭補值。沒有狀態碼的URL將被視為一個302狀態碼。
rewrite
語法:rewrite regex replacement flag
默認值:none
使用字段:server, location, if
按照相關的正則表達式與字符串修改URI,指令按照在配置文件中出現的順序執行。可以在重寫指令后面添加標記。
注意:如果替換的字符串以http://開頭,請求將被重定向,并且不再執行多余的rewrite指令。
尾部的標記(flag)可以是以下的值:
- last – 停止處理重寫模塊指令,之后搜索location與更改后的URI匹配。
- break – 完成重寫指令。
- redirect – 返回302臨時重定向,如果替換字段用http://開頭則被使用。
- permanent – 返回301永久重定向。
rewrite_log
語法:rewrite_log on | off
默認值:rewrite_log off
使用字段:server, location, if
變量:無
啟用時將在error log中記錄notice級別的重寫日志。
set
語法:set variable value
默認值:none
使用字段:server, location, if
為給定的變量設置一個特定值。
uninitialized_variable_warn
語法:uninitialized_variable_warn on|off
默認值:uninitialized_variable_warn on
使用字段:http, server, location, if
控制是否記錄未初始化變量的警告信息。
三. 重寫規則組成部分
3.1 任何重寫規則的第一部分都是一個正則表達式
可以使用括號來捕獲,后續可以根據位置來將其引用,位置變量值取決于捕獲正則表達式中的順序,$1引用第一個括號中的值,$2引用第二個括號中的值,以此類推。如:
^/images/([a-z]{2})/([a-z0-9]{5})/(.*)\.(png|jpg|gif)$
$1是兩個小寫字母組成的字符串,$2是由小寫字母和0到9的數字組成的5個字符的字符串,$3將是個文件名,$4是png、jpg、gif中的其中一個。
3.2 重寫規則的第二部分是URI
請求被改寫。該URI可能包含正則表達式中的捕獲的位置參數或這個級別下的nginx任何配置變量。如:
/data?file=$3.$4
如果這個URI不匹配nginx配置的任何location,那么將給客戶端返回301(永久重定向)或302(臨時重定向)的狀態碼來表示重定向類型。該狀態碼可以通過第三個參數來明確指定。
3.3 重寫規則的第三部分
第三部分也就是尾部的標記(flag)。 last標記將導致重寫后的URI搜索匹配nginx的其他location,最多可循環10次。如:
rewrite '^/images/([a-z]{2})/([a-z0-9]{5})/(.*)\.(png|jpg|gif)$' /data?file=$3.$4 last;
break指令可以當做自身指令。如:
if ($bwhog) { limit_rate 300k; break; }
另一個停止重寫模塊處理指令是return, 來控制主HTTP模塊處理請求。 這意味著,nginx直接返回信息給客戶端,與error_page結合為客戶端呈現格式化的HTML頁面或激活不同的模塊來完成請求。如果狀態碼附帶文字段落,該文本將被放置在響應主體。相反,如果狀態碼后面是一個URL,該URL將成為location頭補值。沒有狀態碼的URL將被視為一個302狀態碼。如:
location = /image404.html { return 404 "image not found\n"; }
四. 實例
http { # 定義image日志格式 log_format imagelog '[$time_local] ' $image_file ' ' $image_type ' ' $body_bytes_sent ' ' $status; # 開啟重寫日志 rewrite_log on; server { root /home/www; location / { # 重寫規則信息 error_log logs/rewrite.log notice; # 注意這里要用‘’單引號引起來,避免{} rewrite '^/images/([a-z]{2})/([a-z0-9]{5})/(.*)\.(png|jpg|gif)$' /data?file=$3.$4; # 注意不能在上面這條規則后面加上“last”參數,否則下面的set指令不會執行 set $image_file $3; set $image_type $4; } location /data { # 指定針對圖片的日志格式,來分析圖片類型和大小 access_log logs/images.log main; root /data/images; # 應用前面定義的變量。判斷首先文件在不在,不在再判斷目錄在不在,如果還不在就跳轉到最后一個url里 try_files /$arg_file /image404.html; } location = /image404.html { # 圖片不存在返回特定的信息 return 404 "image not found\n"; } }
五. 創建新的重新規則
在接到要創建新的重寫規則時,要弄清楚需求是什么樣的,再決定怎么做。畢竟重寫也是耗資源的有效率之分的。 下面的這些問題有些幫助的:
- 你的URL的模式是什么樣的?
- 是否有一個以上的方法來實現?
- 是否需要捕獲URL部分作為變量?
- 重定向到另一個web上可以看到我的規則?
- 是否要替換查詢的字符串參數?
檢查網站或應用程序布局,清楚URL模式。啰嗦一句:我一而再再而三的強調,運維不能與開發脫節,運維要參與到開發當中。如果有不止一種方法實現,創建一個永久重定向。同時,定義一個重寫規范,來使網址清潔,還可以幫助網站更容易被找到。
實例1. 要將home目錄重定向到主頁面上,目錄結構如下:
/ /home /home/ /home/index /home/index/ /index /index.php /index.php/
重寫規則如下:
rewrite ^/(home(/index)?|index(\.php)?)/?$ $scheme: //$host/ permanent;
指定$scheme和$host變量,因為要做一個永久重定向并希望nginx使用相同的參數來構造URL。
實例2. 如果想分別記錄各個部分的URL,可以使用正則表達式來捕獲URI,然后,給變量分配指定位置變量,見上面的實例。
實例3. 當重寫規則導致內部重定向或指示客戶端調用該規則本身被定義的location時,必須采取特殊的動作來避免重寫循環。如:在server配置段定義了一條規則帶上last標志,在引用location時,必須使用break標志。
server { rewrite ^(/images)/(.*)\.(png|jpg|gif)$ $1/$3/$2.$3 last; location /images/ { rewrite ^(/images)/(.*)\.(png|jpg|gif)$ $1/$3/$2.$3 break; } }
實例4. 作為重寫規則的一部分,傳遞新的查詢字符串參數是使用重寫規則的目標之一。 如:
rewrite ^/images/(.*)_(\d+)x(\d+)\.(png|jpg|gif)$ /resizer/$1.$4?width=$2&height=$3? last;
nginx重寫規則說起來挺簡單的,做起來就難,重點在于正則表達式,同時,還需要考慮到nginx執行順序。
原文:http://www.ttlsa.com/nginx/nginx-rewriting-rules-guide/