URL重寫
主要用途為
- 隱藏實體資源路徑與存取。如:防止隨意存取會影響檔案系統、資料庫之檔案
- 簡化URL。例如將常用但冗長之網址縮短為易記之文字
- 舊有URL相容。例如路徑變更、伺服器網址變更時,可將舊網址導向至新網址
最近剛好有這些需要就來玩玩
大部分還是玩Linux的 Apache HTTPD,再說Apache也有Windows版的
跨平台才是王道
Apache HTTPD博大精深,就算是mod_rewrite一、兩天還是只能窺見點皮毛而已
目前大概將基本款規則看過,配合「.htaccess」檔案建置Per-Directory重寫
本文列出基本常用的Per-directory URL重寫介紹,足夠應付大部分狀況
進階版本的例如Proxy、SSL還請參考官方文件[1] 章節
Apache HTTPD的 URL重寫主要由動態模組 mod_rewrite負責
mod_rewrite的執行分為 Per-server與 Per-directory兩種
Per-server設定將寫於http.conf以及 <VirtualHost> (註1) 區塊當中
Per-directory設定將寫於<Directory>區塊及特定目錄下 .htaccess檔案當中
URL重寫最基本款主要由一條條的重寫條件(Condition)與重寫規則(Rule)構成
重寫的運作則由一次次的迭代逐次改寫,直至兩次重寫結果相同為止
當然最大迭代次數是有限制的,以防迭代無法收斂而造長無窮迴圈
此時伺服器將會送出500 Internal Server Error [3]
mod_rewrite重寫流程大致如下
重寫規則可與Condition搭配,或者獨立存在
mod_rewrite提供下列幾種變數類型
可用於重寫之條件判斷,或展開於重寫之結果當中
1. 伺服器變數 (Server Variable)
由Apache伺服器提供之資訊,存取方式為 %{變數名稱}
- HTTP標頭 (Header) 相關
HTTP_USER_AGENT: 指示網頁瀏覽軟體之相關資訊
同JavaScript的navigator.userAgent
HTTP_REFERER: 指示網址之來源URL
此欄位在點擊外部連結(<a>標籤)
或頁面中索取其他資源(如圖片、JavaScript) 時
將會附加於索取資源的HTTP Header中,指示索取資源之頁面URL
HTTP_COOKIE: Cookie內容
HTTP_HOST: 沒意外為伺服器之IP或網域名稱 (伺服器名稱)
- 連線與要求 (Connection & Request) 相關
以「http://example.com/~test/index.html?q1=2」為例
REMOTE_ADDR: 客戶端IP位址
REMOTE_HOST: 客戶端電腦名稱 (IP或網域名稱)
REMOTE_PORT: 客戶端電腦連線開啟之連接埠
REQUEST_METHOD: HTTP要求方法,如GET、POST等
SCRIPT_FILENAME: 檔案系統資源路徑 (非URL而是經過解析過之檔案路徑位置)
範例即 /home/test/index.html
QUERY_STRING: 問號後之部分
範例即 q1=2 - 伺服器內部資訊
DOCUMENT_ROOT: 伺服器文件存取根目錄之位置
SERVER_ADMIN: 特權使用者帳號名稱,基本上為root
SERVER_NAME
SERVER_ADDR
SERVER_PORT: 伺服器監聽之連接埠
SERVER_PROTOCOL: 使用之通訊協定與版本,沒意外為 HTTP/1.1 - 時間,如字面意思
TIME_YEAR、TIME_MON、TIME_DAY
TIME_HOUR、TIME_MIN、TIME_SEC
TIME_WDAY、TIME - 特殊
API_VERSION
THE_REQUEST: HTTP要求開頭,如 GET /index.html HTTP/1.1,不含其他要求標頭值
REQUEST_URI: 範例即 /~test/index.php (不包含 Query String)
REQUEST_FILENAME: 值同SCRIPT_FILENAME
2. 環境變數
可讀取任何環境變數值,存取方式為 %{ENV:變數名稱}
自然也可以設定環境變數,見URL重寫規則章節
例如:%{ENV:PATH}
注意,每進入一新的pass,前一輪之所有環境變數會被加上「REDIRECT_」前綴字串
因此每經過一輪,所有環境變數的名稱將會越變越長,官方文件並未正式記載此運作
3. HTTP標頭值
可讀取任何HTTP標頭欄位值,存取方式為 %{HTTP:標頭欄位名稱}
如 %{HTTP:Host},即讀取HTTP標頭 Host欄位值
4. Backreference
以 $N 或 %N 作存取,1 ≦ N ≦ 9,主要用於字串比較並取得比較結果
字串比較採用 PCRE (Peral-Compatible Regular Expression) [2]
在重寫條件與重寫規則當中皆會使用到
%N用於重寫條件,$N則用於重寫規則
N則代表第N個 matching group,PCRE的matching group由 () 構成
例如 ABC與 ^(A(B))(C)$ 作比較
Group 1為 AB;Group 2為 B;Group3為 C
根據官方文件,URL要能夠運作
首先必須撰寫「RewriteEngine On」與「Options FollowSymLinks」
但我的case似乎可以不需要
且預設裝載的Apache也把許多指令都限制不可使用
本範例所要達成的目標為:
- 將沒有指定版本編號之URL前頭加上預設之版本編號目錄
- 將使用HTTP GET Method之URL,導向至Resource「目錄」下
- 將使用HTTP Post Method之URL,導向至Request「目錄」下
- 將 request「目錄下」特定資源,如,icon、javascript、css導向至專用目錄下
- 將其他URL,當作一般網頁,導向至版本目錄下之index.php
並加上query string,method為request或resource
並path為Resource或Request「目錄」後之部分 - 禁止直接存取index.php
本範例將越接近最後結果放置於較前之位置
理論上可在使用者直接輸入接近最後結果之URL時,加快處理速度
RewriteEngine ON RewriteBase /~example/ # NOTICE: !! All environment variables of previous pass is prefixed with 'REDIRECT_' RewriteCond %{ENV:REDIRECT_FINAL} =true RewriteRule .* - [L] # Prevent directly access to the result path RewriteCond %{ENV:REDIRECT_FINAL} !=true RewriteCond %{REQUEST_URI} ^/~crwp/v[0-9]+.[0-9]+/index.* RewriteRule .* - [F] # Final Rewrite # Only the matching URL will be rewritten # icon/image resource rewrite RewriteRule ^(v[0-9]+.[0-9]+)/Resource/(icon|icons|img|image|images)/(.+\.(jpg|jpeg|gif|png))$ \ $1/image/$3 [NC,L,E=FINAL:true] # javascript resource rewrite RewriteRule ^(v[0-9]+.[0-9]+)/Resource/(js|javascript|jslib)/(.+(\.js|\.map))$ \ $1/js/$3 [NC,L,E=FINAL:true] # CSS resource rewrite RewriteRule ^(v[0-9]+.[0-9]+)/Resource/(css)/(.+\.css)$ $1/css/$3 [NC,L,E=FINAL:true] # jsrender template RewriteRule ^(v[0-9]+.[0-9]+)/Resource/(jtmpl|jhtml|jsrender)/(.+\.jhtml)$ \ $1/jhtml/$3 [NC,L,E=FINAL:true] # Others RewriteRule ^(v[0-9]+.[0-9]+)/(Request|Resource)/(.*)$ $1/index.php?method=$2&path=$3 [NC,L,E=FINAL:true] # Set POST method as Request RewriteCond %{REQUEST_METHOD} =POST RewriteCond %{REQUEST_URI} !^/~crwp/v[0-9]+.[0-9]+/request(/.*)?$ [NC] RewriteRule ^(v[0-9]+.[0-9]+)/(.*)$ $1/Request/$2 [NC,L] # Set GET method as Resource RewriteCond %{REQUEST_METHOD} =GET RewriteCond %{REQUEST_URI} !^/~crwp/(v[0-9]+.[0-9]+)/Resource(/.*)?$ [NC] RewriteRule ^((v[0-9]+.[0-9]+)/(.*))$ $2/Resource/$3 [NC,L] # add default version number RewriteCond %{REQUEST_URI} !^/~crwp/v[0-9]+.[0-9]+(/.*)?$ [NC] RewriteRule ^.*$ v2.0/$0 [L] |
執行指令為 RewriteCond
依官方文件語法為 RewriteCond TestString Pattern [Flags]
TestString為必須測試之文字,基本上為使用變數
Pattern為比較之字樣,為PCRE語法
但亦包含下列特殊比較字元
= : 比較TestString是否與Pattern完全相等,參照C語言 strcmp
> : 比較TestString是否字串比較大於Pattern,參照C語言 strcmp
< : 比較TestString是否字串比較小於Pattern,參照C語言 strcmp
-d: 比較TestString是否為目錄
-f : 比較TestString是否為一般檔案
-s : 比較TestString是否為一般非空檔案
-l : 比較TestString是否為Symbol Link (以Windows觀念即捷徑)
-x : 比較TestString是否為有執行權限之檔案
若在Pattern開頭加上「!」符號,代表「非」、不符合條件
注意在一個pass中所有伺服器變數值是不變的,並不會因為重寫而改變
Flag為由逗點分隔之符號列表,可包含下列
NC (No Case)
字串比較不管大小寫
OR (邏輯串接OR)
以OR條件串接下列相鄰之RewriteCond,若不加則為 AND
範例:
# REQUEST_URI不管大小寫者且為 /~test/x 開頭,或
RewriteCond %{REQUEST_URI} ^/~test/x [OR, NC]
# REQUEST_URI為 .jpg結尾 (「.」在PCRE中有特殊意義,「\」逃脫字元是必要的)
RewriteCond %{REQUEST_URI} \.jpg$
指令為RewriteRule
依官方文件語法為 RewriteRule Pattern Substitution [Flag]
Pattern,重寫會先進行字串比較,若符合才進行內容替換及 Flag運行
有時可以簡化RewriteCond的撰寫
與Pattern比較的內容,在Per-Server的狀況下為URI部分
即主機網址後的部分,不包括Query String
在Per-Directory的狀況下,則為移除 Prefix (由RewriteBase指定) 之後的部分
Substition則為替換之內容,URL重寫會將原有內容完全替換為 Substition內容
最後再加上Prefix前綴字樣,成為新的URL
Substition可為
- 檔案系統路徑,僅Per-Server可用
- URI,替換的內容為相對於RewriteBase之路徑,若沒有指定
則相對於 Document Root。若URI為實際存在之檔案,則
Apache伺服器就不會再做進一步的URL解析。可含Query String - 絕對URL,即包含protocol、主機位址與連接埠之字串
如 http://example:80/test.html,可含Query String - 「-」,即不進行改寫
Flag為逗點分隔之符號列表,包含下列
C : chain
即將下一相鄰之重寫規則結合為一群組
由於RewriteCond符合時預設僅與一個RewriteRule結合
使用C Flag則可將多個RewriteRule與 RewriteCond結合
即 C語言的 if (condition) { rule1; rule2; ...}
CO : cookie
設定 cookie,設定語法為 CO={cookie名稱}:{cookie值}
E : environment
設定環境變數,設定語法為 E={環境變數名稱}:{環境變數值}
若為 E=!{環境變數名稱},則刪除該環境變數
F : forbidden
mod_rewrite 將立即結束所有rewrite程序
並回傳 HTTP 403 Forbidden錯誤狀態回應 [3]
G : gone
mod_rewrite 將立即結束所有 rewrite程序
並回傳 HTTP 410 GONE錯誤狀態回應 [3]
L : last rule
官方文件記載結束重寫程序,忽略後續之RewriteRule
此Flag為最易被誤解,事實上L Flag為結束目前的Pass (結束這回合)
並進入下一輪,而非結束整個重寫程序
N :next
當RewriteRule Pattern相符時,重複執行此Rule
即C語言 while (pattern match) substitue;
NC: no case
字串比較時忽略大小寫
QSA:Query String Append
將原有URL之Query String附加於重寫結果上。
R : Redirect
結束所有重寫程序,並回傳 HTTP重新導向狀態碼
使用格式 R=3xx,預設為302。
HTTP所定義之重新導向狀態碼,為3xx。[3]
一般使用 301 (永久重新導向)或 302 (暫時重新導向)
使用 R flag之後會發現,與不重新導向之運作不同
網頁瀏覽器網址將變更為重寫之網址 (此將可能無法達成隱藏實體資源之目的)
並且重新向Apache發送HTTP要求,並重新進行重寫程序
S :skip
使用格式為 S=N
指跳過後續N個RewriteRule,類似C語言的goto
RewriteBase
當網站不在Document Root之下時
例如個人家目錄 (預設為 /home/*/public_html) 或透過 Alias 轉換的目錄
當此狀況若不使用RewriteBase指令
一切的重寫結果都會當作在 Document Root底下之URL,而使URL存取錯誤
注意 RewriteBase之內容必定被當成目錄,而非字樣
因此若結尾未加上「/」,重寫後之結果將成為 {base}/rewrite
但若結尾加上「/」,RewriteRule Pattern所比較的字串便非「/」開頭
1. Name-based Virtual Host,依不同domain name提供不同document root位置服務
在一台伺服器上營造有多台伺服器的感覺。
[1] Apache 2.2 mod_rewrite document
留言列表