rewrite

伺服器重寫技術

Rewrite是一種伺服器的重寫技術,它可以使得伺服器可以支持 URL 重寫,是一種最新流行的伺服器技術。它還可以實現限制特定IP訪問網站的功能。

Rewrite介紹


很多情況下,某個 IP 的訪問很容易造成 CPU 100% (比如 某些搜索引擎的固定抓取,別人大量的採集站點),這個時候我們就要利用一些有效的手段封掉對方的 IP,讓他無法消耗伺服器的資源,封 IP 的方法有很多種,如果你的 Web 伺服器安裝了 Rewrite 模塊的話,也可以試試利用 Rewrite 規則封掉對方的 IP。
例如我們把某個特定的 IP 直接重定向到 baidu 首頁,在網站根目錄的 .htaccess 文件里添加代碼:
Code:
RewriteCond % 123.123.123.123 [NC]
RewriteRule ^(.*)$ http://www.baidu.com/$1 [R=301]
將 123.123.123.123 這個 IP 替換成您要限制的 IP 即可。如果要實現多個 IP ,可以這樣寫:
RewriteCond % 123.123.123.123 [OR]
RewriteCond % 124.124.124.124 [NC]
RewriteRule ^(.*)$ http://www.baidu.com/$1 [R=301]

Rewrite主要的功能

Rewrite主要的功能就是實現URL的重寫。它的正則表達式是基於Perl語言,入站的規則用於修改 HTTP 請求 Url。這些規則可以為以下幾個目的,如演示對用戶更加友好的 URL 命名空間為您的 Web 站點,將請求的 Url 重定向到新位置,或阻止訪問 Url 來提供服務。出站的重寫規則修改 HTTP 響應。例如,如果您的 Web 站點的導航結構已更改,您可以創建修改您的內容中的 Url,以便將 Web 頁的內容指向正確的位置出站規則。然後,您可以創建基於緩存的位置與新的 url 的客戶端請求重定向的入站的規則。可基於伺服器級的(httpd.conf)和目錄級的(.htaccess)兩種方式。如果要想用到rewrite模塊,必須先安裝或載入rewrite模塊。方法有兩種一種是編譯apache的時候就直接安裝rewrite模塊,別一種是編譯apache時以DSO模式安裝apache,然後再利用源碼和apxs來安裝rewrite模塊。
基於伺服器級的(httpd.conf)
有兩種方法,一種是在httpd.conf的全局下直接利用RewriteEngine on來打開rewrite功能;另一種是在局部里利用RewriteEngine on來打開rewrite功能,下面將會舉例說明,需要注意的是,必須在每個virtualhost里用RewriteEngine on來打開rewrite功能。否則virtualhost里沒有RewriteEngine on它裡面的規則也不會生效。
基於目錄級的(.htaccess)
要注意一點那就是必須打開此目錄的FollowSymLinks屬性且在.htaccess里要聲明RewriteEngine on。

如何使用Rewrite 功能(Apache)

一。我們通過在IIS中安裝一個名為 ISAPI_Rewrite 的ISAPI篩選器來實現 Rewrite 功能,您需要做的事情只有一個,就是修改配置文件 httpd.ini ,有關該組件的詳細使用方法,您可以登錄該組件官方網站 http://www.somesite.com/ 來學習,這裡我們舉一個簡單的例子來說明它的用法。
假設您要實現這樣的 Rewrite 功能:您希望當用戶訪問 /about.htm (您的空間里可以並不需要存在 about.htm)的時候實際訪問的是 /index.html。
設置方法是:
1、創建一個文本文件,內容為
[ISAPI_Rewrite]
RewriteRule /about\.htm /index\.html
這裡,RewriteRule 這一行即為規則行,這一行由三部分組成,三部分由空格隔開,第一部分即 RewriteRule 這幾個字,第二部分為用戶訪問的地址(使用正則表達式),第三部分為實際存在於伺服器上的文件路徑。
2、將上述文件保存,命名為 httpd.ini。
3、將這個文件上傳到您的網站根目錄中,對於我們的虛擬主機,即上傳至 /web 文件夾中。
這時,當您訪問 about.htm 的時候,看到的就是 index.html 的內容。
Rewrite 是一個功能強大的平台,要真正的使用它,您可能要花費相當長的時間來學習。如果您使用像 Discuz! 論壇等支持偽靜態的系統,而僅僅是需要使用偽靜態功能,那麼您可以不必學習,直接複製論壇開發者提供的配置文件即可,但要注意配置文件必須命名為 httpd.ini ,並且這個文件必須放在網站的根目錄下。
httpd.ini 修改或者上傳后一般會立即生效,如果長時間不能生效,請登錄主機控制面板將網站停止然後再啟動。
評述:不建議修改http.conf文件,因為這樣需要重新啟動apache。建議在項目的根目錄建立一個文檔,文檔名字就是.htaccess,然後把重寫規則寫進去,這樣就不需要重啟apache。
二、怎樣藉助 Rewrite 功能為特定的域名顯示特定文件夾中的內容?
這實際上是 Rewrite 功能的巧用,因為我們既然可以將 /about.htm 重寫為 /index.html ,我們也就可以把 /about.htm 重寫為 /about/。下面我們講具體的實現方法。
我們假設您有兩個域名 www.name1.com 和 www.name2.com ,我們實現讓訪問 www.name1.com 時看到的是網站根下的內容,而訪問 www.name2.com 時看到的是 /name2/ 文件夾中的內容。
那麼設置步驟如下:
1、將您的網站捆綁域名 www.name1.com 和 www.name2.com ,這個非常重要,兩個域名都要捆綁。這時候如果不做設置,兩個域名訪問的都是網站根下。
2、修改上述問題一中的 httpd.ini 文件,在尾部增加以下內容:
RewriteCond Host: www\.name2\.com
# 表示下一行規則只對 www.name2.com 生效(正則表達式中 \. 表示 . 本身)。
RewriteRule ^(.*)$ /name2/$1 [I]
# 表示將所有網頁 Rewrite 到 name2 文件夾中,[I]表示忽略大小寫。
請確認上述內容位於 [ISAPI_Rewrite] 行之下,如果原本網站不存在 httpd.ini 文件,請將上述內容前面加上 [ISAPI_Rewrite]。
3、將修改後的 httpd.ini 上傳到網站根下覆蓋原文件。
通過這個方法,您可以將捆綁在網站上的任何一個域名採用任何特定的 Rewirte 規則,實現類似捆綁子目錄的功能當然也是不在話下。這樣,您的空間能捆綁多少個域名,您就可以建立多少個內容不同的網站了。

如何使用Rewrite 功能(nginx)

ngx_http_rewrite_module模塊允許正則替換URI,返回頁面重定向,和按條件選擇配置。
ngx_http_rewrite_module模塊指令按以下順序處理:
• 處理在server級別中定義的模塊指令;
• 為請求查找location;
• 處理在選中的location中定義的模塊指令。如果指令改變了URI,按新的URI查找location。這個循環至多重複10次,之後nginx返回錯誤500 (Internal Server Error)。
指令
語法:break;
默認值:
上下文:server,location,if
停止處理當前這一輪的ngx_http_rewrite_module指令集。
舉例:
if ($slow) { limit_rate 10k; break;}
語法:if(condition) { ... }
默認值:
上下文:server,location
計算指定的condition的值。如果為真,執行定義在大括弧中的rewrite模塊指令,並將if指令中的配置指定給請求。if指令會從上一層配置中繼承配置。
條件可以是下列任意一種:
• 變數名;如果變數值為空或者是以“0”開始的字元串,則條件為假;
• 使用“=”和“!=”運算符比較變數和字元串;
• 使用“~”(大小寫敏感)和“~*”(大小寫不敏感)運算符匹配變數和正則表達式。正則表達式可以包含匹配組,匹配結果後續可以使用變數$1..$9引用。如果正則表達式中包含字元“}”或者“;”,整個表達式應該被包含在單引號或雙引號的引用中。
• 使用“-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;}
內嵌變數$invalid_referer的值是通過valid_referers指令設置的。
語法:
returncode[text];
returncodeURL;
returnURL;
默認值:
上下文:server,location,if
停止處理並返回指定code給客戶端。返回非標準的狀態碼444可以直接關閉連接而不返迴響應頭。
從0.8.42版開始,可以在指令中指定重定向的URL(狀態碼為301、302、303和307),或者指定響應體文本(狀態碼為其它值)。響應體文本或重定向URL中可以包含變數。作為一種特殊情況,重定向URL可以簡化為當前server的本地URI,那麼完整的重定向URL將按照請求協議($scheme)、server_name_in_redirect指令和port_in_redirect指令的配置進行補全。
另外,狀態碼為302的臨時重定向使用的URL可以作為指令的唯一參數。該參數應該以“http://”、“https://”或者“https://”開始。URL中可以包含變數。
0.7.51版本以前只能返回下面狀態碼: 204、400、402— 406、408、410、411、413、416 和 500— 504。
直到1.1.16和1.0.13版,狀態碼307才被認為是一種重定向。
語法:rewriteregexreplacement[flag];
默認值:
上下文:server,location,if
如果指定的正則表達式能匹配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的永久重定向。
完整的重定向URL將按照請求協議($scheme)、server_name_in_redirect指令和port_in_redirect指令的配置進行補全。
舉例:
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:
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;
如果正則表達式中包含字元“}”或者“;”,整個表達式應該被包含在單引號或雙引號的引用中。
語法:rewrite_logon|off;
默認值:rewrite_log off;
上下文:http,server,location,if
開啟或者關閉將ngx_http_rewrite_module模塊指令的處理日誌以notice級別記錄到錯誤日誌中。
語法:setvariablevalue;
默認值:
上下文:server,location,if
為指定變數variable設置變數值value。value可以包含文本、變數或者它們的組合。
語法:uninitialized_variable_warnon|off;
默認值:uninitialized_variable_warn on;
上下文:http,server,location,if
控制是否記錄變數未初始化的警告到日誌。
內部實現
ngx_http_rewrite_module模塊的指令在解析配置階段被編譯成nginx內部指令。這些內部指令在處理請求時被解釋執行。而解釋器是一個簡單的堆棧機器。
比如,下面指令
location /download/ { if ($forbidden) { return 403; } if ($slow) { limit_rate 10k; } rewrite ^/(download/.*)/media/(.*)\..*$ /$1/mp3/$2.mp3 break;}
將被翻譯成下面這些指令:
variable $forbiddencheck against zero return 403 end of codevariable $slowcheck against zeromatch of regular expressioncopy "/"copy $1copy "/mp3/"copy $2copy ".mp3"end of regular expressionend of code
請注意沒有對應上面的limit_rate指令的內部指令,因為這個指令與ngx_http_rewrite_module模塊無關。nginx會為這個if塊單獨創建一個配置,包含limit_rate等於10k。如果條件為真,nginx將把這個配置指派給請求。
指令
rewrite ^/(download/.*)/media/(.*)\..*$ /$1/mp3/$2.mp3 break;
可以通過將正則表達式中的第一個斜線“/”放入圓括弧,來實現節約一個內部指令:
rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 break;
對應的內部指令將會是這樣:
match of regular expressioncopy $1copy "/mp3/"copy $2copy ".mp3"end of regular expressionend of code