Apache的Mod_rewrite學習(一)
車東很早就寫了一篇文章來介紹利用mod_rewrite模組來達到用靜態頁面形式的連結隱藏背景動態網頁面。
Apache的rewrite模組,提供了一個基於規則的重寫(rewrite,也許譯為重構更為合適)引擎,來即時重寫發送到Apache的請求URL。因功能極其強大,被稱為URL重寫的“瑞士軍刀”。
這個模組使用一個基於Regex解析器開發的重寫引擎,根據web管理員定義的規則來即時(on the fly)重寫請求URL。它支援任意數目的重寫規則,以及附加到一條規則上的任意數目的規則條件,從而提供了一套非常靈活和功能強大的URL處理機制。URL處理操作的實施與否,依賴於各種各樣的條件檢查,如檢查伺服器變數、環境變數、HTTP頭欄位、時間戳記的值,甚至外部資料庫的檢索結果。這個模組可以在伺服器範圍內(http.conf)、目錄範圍內(.htaccess)或請求串(query-string)的一部分處理有關的URL。重寫的結果URL,可以指向一個站內的處理常式、指向站外的重新導向或者一個站內的代理。與靈活和功能強大相隨的是設定的複雜,別指望一天內弄明白整個模組。(所以,這個學習筆記也分了幾部分:)
內部處理過程
API階段
首先,Apache處理HTTP請求是分階段進行的,Apache API為每個階段提供了一個鉤子(hook)。Mod_rewrite使用了其中的兩個鉤子:一個用來在HTTP請求被讀取但還沒有訪問授權驗證之前進行URL_to_filename轉換,一個用來在授權驗證完成且目錄設定檔案(.htaccess)讀取之後、但內容處理器(content handler)被調用之前激化,進行修補(fixup).因此,當一個請求到達,Apache決定了相關的伺服器(或虛擬伺服器)以後進行URL_to_filename階段,重寫引擎(rewrite engine)開始處理伺服器設定中的重寫指令(mod_rewrite directives).接下來幾個階段過後進入修補階段,此時最終的資料所在的物理目錄已經找到,目錄配置中的重寫指令開始執行。在這兩個階段,mod_rewrite都是將URL重寫為新的URL或檔案名稱,所以看起來並沒有明顯的區別。對API的這種應用,並不是一開始就是這樣設計的,而是Apache1.x不得已而為之。為了搞清這個問題,以下兩點需要記住。
1)雖然mod_rewrite能進行URL到URL、URL到檔案名稱字甚至檔案名稱字到檔案名稱字的轉換,API(1.x)目前提供了一個URL_to_filename轉換。在Apache2.0中,這兩個鉤子會被加進去,整個過程會更加清晰。一個事實必須清楚的記得:Apache在URL_to_filename鉤子中,做得比API設計的功能更多。
2)不可思議的是,mod_rewrite能在目錄範圍內(如根據.htaccess檔案的指令配置)進行URL處理,雖然URL很早就已經被轉換為檔案名稱字了。只所以會如此,是因為.htaccess檔案存在於檔案系統中。也就是說,在這個階段來進行URL處理,是非常晚的時候了。為瞭解決這個"先有雞還是先有蛋"的問題,mod_rewrite用了一個小技巧:當在目錄範圍內處理URL/filename時,mod_rewrite先將檔案名稱逆轉回相關的URL(雖然通常是不可能的,但請參見下面用以實現這個技巧的RewriteBase指令),然後據這個新URL產生一個站內的子請求(internal sub-request),這又重開始了API進程。Mod_rewrite盡量使這些複雜的步驟對使用者透明,但應要記住:雖然目錄範圍URL的真正處理過程很快很高效,但這一階段會因為這個"雞和蛋"的問題而變得很慢和低效。從另一方面來看,這也是mod_rewrite提供給普通使用者進行目錄範圍內的URL處理的唯一途徑.
規則集(RewriteRule指令集合)處理過程
當mod_rewrite在上述的兩個API階段被啟用時,它會從它的配置資料結構(在開始伺服器上下文(per-server context)或目錄上下文(per-directory context)時建立的)中讀取配置的規則集,然後URL重寫引擎啟動來執行包含的規則集(一個或多條規則以及它們的條件)。兩種上下文中的處理過程都是一樣的,差別只是在最後的結果處理過程上。
規則集中規則的順序是非常重要的,因為重寫引擎以特定的順序來處理它們。重寫引擎順序遍曆規則集,當一條規則匹配時,引擎會去遍曆與它相關的條件集(RewriteCond指令集合).由於曆史的原因,條件集先被列出來,因此控制流程流程有點曲折(long-winded).一所示:
正如所看到的,首先URL會與每條規則的模板(pattern)比較,當匹配失敗時,立即停止對當前規則的處理進入下一條規則。當匹配成功時,mod_rewrite尋找相關的規則條件。如果找不到相關的條件,則直接執行規則中定義的替換,然後回到規則遍曆的過程。如果找到了相關的條件,則啟動一個內部迴圈,依次檢查各個條件。對於檢查,我們不是拿一個模板來匹配當前的URL,而是先建立一個TestString串,將串內的變數、後向引用(bakc-reference)、查詢結果(map lookups)等展開,然後用這個TestString和條件式中的CondPattern進行匹配,如果匹配失敗,則整個條件集且這個規則都不再執行,重要回到規則遍曆中;如果匹配成功,則檢查下一個條件,如果所有的條件都滿足,則執行規則中定義的替換動作。
特殊字元的轉義
既然基於正則式,則當然會有特殊字元的問題。在1.3.20版本的Apache中,通過在特殊字元前加一個“/”來將TestString或Sustitution串的特殊字元轉義。
正則式的後向引用
有一點需要記住:一旦在模板(pattern)或條件模板(CondPattern)中使用了括弧,則後向引用已經自動產生了,你可以在Sustitution或TestString中通過$N或%N來引用相關的值。,描述了後向引用的值可以傳到的位置。
配置指令(Configuration Directives)
| 指令 |
文法 |
預設值 |
說明 |
備忘 |
| RewriteEngine |
RewriteEngine on|off |
Off |
開關重構引擎 |
預設時不能繼承,故每個虛擬機器主機都要有自己的開關指令。 |
| RewriteOptions |
RewriteOptions Option |
MaxRedirects=10 |
設定一些特殊參數 |
inherit:配置是否繼承,MaxRedirects=number:內部重新導向次數 |
| RewriteLog |
RewriteLog file-path |
None |
設定重寫log檔案 |
用RewriteLogLevel 0來禁止日誌 |
| RewriteLogLevel |
RewriteLogLevel Level |
RewriteLogLevel 0 |
設定記錄層級 |
0表示沒有,2以上用於debug,9及以上表示全部資訊 |
| RewriteLock |
RewriteLock file-path |
None |
設定RewriteMap程式的同步鎖檔案 |
要求是本地檔案,此檔案只對rewriting map-program有效。 |
| RewriteMap |
RewriteMap MapName MapType:MapSource |
Notused per default |
定義重寫影射 |
具體說明參見文檔 |
| RewriteBase |
RewriteBase URL-path |
physical directory path |
設定目錄範圍內重寫的基本URL |
具體說明參見文檔 |
| RewriteCond |
RewriteCond TestString CondPattern |
None |
定義規則條件 |
具體說明參見文檔 |
| RewriteRule |
RewriteRule Pattern Substitution |
None |
定義重寫規則 |
具體說明參見文檔 |
參考資料:
http://httpd.apache.org/docs/mod/mod_rewrite.html
Apache的Mod_rewrite學習(二)
今天學習重寫規則的文法。
RewriteRule
Syntax: RewriteRule Pattern Substitution [flags]
一條RewriteRule指令,定義一條重寫規則,規則間的順序非常重要。對Apache1.2及以後的版本,模板(pattern)是一個POSIX正則式,用以匹配當前的URL。當前的URL不一定是用記最初提交的URL,因為可能用一些規則在此規則前已經對URL進行了處理。
對mod_rewrite來說,!是個合法的模板首碼,表示“非”的意思,這對描述“不滿足某種匹配條件”的情況非常方便,或用作最後一條預設規則。當使用!時,不能在模板中有分組的萬用字元,也不能做後向引用。
當匹配成功後,Substitution會被用來替換相應的匹配,它除了可以是普通的字串以外,還可以包括:
- $N,引用RewriteRule模板中匹配的相關字串,N表示序號,N=0..9
- %N,引用最後一個RewriteCond模板中匹配的資料,N表示序號
- %{VARNAME},伺服器變數
- ${mapname:key|default},映射函數調用
這些特殊內容的擴充,按上述順序進行。
一個URL的全部相關部分都會被Substitution替換,而且這個替換過程會一直持續到所有的規則都被執行完,除非明確地用L標誌中斷處理過程。
當susbstitution有”-”首碼時,表示不進行替換,只做匹配檢查。
利用RewriteRule,可定義含有請求串(Query String)的URL,此時只需在Sustitution中加入一個?,表示此後的內容放入QUERY_STRING變數中。如果要清空一個QUERY_STRING變數,只需要以?結束Substitution串即可。
如果給一個Substitution增加一個http://thishost[:port]的首碼,則mod_rewrite會自動將此首碼去掉。因此,利用http://thisthost做一個無條件的重新導向到自己,將難以奏效。要實現這種效果,必須使用R標誌。
Flags是選擇性參數,當有多個標誌同時出現時,彼此間以逗號分隔。
- 'redirect|R [=code]' (強制重新導向)
給當前的URI增加首碼http://thishost[:thisport]/, 從而產生一個新的URL,強制產生一個外部重新導向(external redirection,指生的URL發送到用戶端,由用戶端再次以新的URL發出請求,雖然新URL仍指向當前的伺服器). 如果沒有指定的code值,則HTTP應答以狀態值302 (MOVED TEMPORARILY),如果想使用300-400(不含400)間的其它值可以通過在code的位置以相應的數字指定,也可以用標誌名指定: temp (預設值), permanent, seeother.
注意,當使用這個標誌時,要確實substitution是個合法的URL,這個標誌只是在URL前增加http://thishost[:thisport]/首碼而已,重寫操作會繼續進行。如果要立即將新URL重新導向,用L標誌來中重寫流程。
- 'forbidden|F' (強制禁止訪問URL所指的資源)
立即返回狀態值403 (FORBIDDEN)的應答包。將這個標誌與合適的RewriteConds 聯合使用,可以阻斷訪問某些URL。
- 'gone|G' (強制返回URL所指資源為不存在(gone))
立即返回狀態值410 (GONE)的應答包。用這個標誌來標記URL所指的資源永久消失了.
- # 'proxy|P' (強制將當前URL送往代理模組(proxy module))
這個標誌,強制將substitution當作一個發向代理模組的請求,並立即將共送往代理模組。因此,必須確保substitution串是一個合法的URI (如, 典型的情況是以http://hostname開頭),否則會從代理模組得到一個錯誤. 這個標誌,是ProxyPass指令的一個更強勁的實現,將遠程請求(remote stuff)映射到本機伺服器的名字空間(namespace)中來。
注意,使用這個功能必須確保代理模組已經編譯到Apache 伺服器程式中了. 可以用“httpd -l ”命令,來檢查輸出中是否含有mod_proxy.c來確認一下。如果沒有,而又需要使用這個功能,則需要重新編譯``httpd''程式並使用mod_proxy有效。
- 'last|L' (最後一條規則)
中止重寫流程,不再對當前URL施加更多的重寫規則。這相當於perl的last命令或C的break命令。
- 'next|N' (下一輪)
重新從第一條重寫規則開始執行重寫過程,新開的過程中的URL不應當與最初的URL相同。 這相當於Perl的next命令或C的continue命令. 千萬小心不要產生死迴圈。
- # 'chain|C' (將當前的規則與其後續規則綑綁(chained))
當規則匹配時,處理過程與沒有綑綁一樣;如果規則不匹配,則綑綁在一起的後續規則也不在檢查和執行。
- 'type|T=MIME-type' (強制MIME類型)
強制將目標檔案的MIME-type為某MIME類型。例如,這可用來模仿mod_alias模組對某目錄的ScriptAlias指定,通過強制將該目錄下的所有檔案的類型改為 “application/x-httpd-cgi”.
- 'nosubreq|NS' (used only if no internal sub-request )
這個標誌強制重寫引擎跳過為內部sub-request的重寫規則.例如,當mod_include試圖找到某一目錄下的預設檔案時 (index.xxx),sub-requests 會在Apache內部發生. Sub-requests並非總是有用的,在某些情況下如果整個規則集施加到它上面,會產生錯誤。利用這個標誌可排除執行一些規則。
- 'nocase|NC' (模板不區分大小寫)
這個標誌會使得模板匹配當前URL時忽略大小寫差別。
- 'qsappend|QSA' (追加請求串(query string))
這個標誌,強制重寫引擎為Substitution的請求串追加一部分串,則不是替換掉原來的。藉助這個標誌,可以使用一個重寫規則給請求串增加更多的資料。
- 'noescape|NE' (不對輸出結果中的特殊字元進行轉義處理)
通常情況下,mod_write的輸出結果中,特殊字元(如'%', '$', ';', 等)會轉義為它們的16進位形式(如分別為'%25', '%24', and '%3B')。這個標誌會禁止mod_rewrite對輸出結果進行此類操作。 這個標誌只能在 Apache 1.3.20及以後的版本中使用。
- 'passthrough|PT' (通過下一個處理器)
這個標誌強制重寫引擎用filename欄位的值來替換內部request_rec資料結構中uri欄位的值。. 使用這個標誌,可以使後續的其它URI-to-filename轉換器的Alias、ScriptAlias、Redirect等指令,也能正常處理RewriteRule指令的輸出結果。用一個小例子來說明它的語義:如果要用mod_rewrite的重寫引擎將/abc轉換為/def,然後用mod_alas將/def重寫為ghi,則要:
RewriteRule ^/abc(.*) /def$1 [PT]
Alias /def /ghi
如果PT標誌被忽略,則mod_rewrite也能很好完成工作,如果., 將 uri=/abc/... 轉換為filename=/def/... ,完全符合一個URI-to-filename轉換器的動作。接下來 mod_alias 試圖做 URI-to-filename 轉換時就會出問題。
注意:如果要混合都含有URL-to-filename轉換器的不同的模組的指令,必須用這個標誌。最典型的例子是mod_alias和mod_rewrite的使用。
- 'skip|S=num' (跳過後面的num個規則)
當前規則匹配時,強制重寫引擎跳過後續的num個規則。用這個可以來模仿if-then-else結構:then子句的最後一條rule的標誌是skip=N,而N是else子句的規則條數。
- 'env|E=VAR:VAL' (設定環境變數)
設定名為VAR的環境變數的值為VAL,其中VAL中可以含有正則式的後向引用($N或%N)。這個標誌可以使用多次,以設定多個環境變數。這兒設定的變數,可以在多種情況下被引用,如在XSSI或CGI中。另外,也可以在RewriteCond模板中以%{ENV:VAR}的形式被引用。
-
注意:一定不要忘記,在伺服器範圍內的設定檔中,模板(pattern)用以匹配整個URL;而在目錄範圍內的設定檔中,目錄首碼總是被自動去掉後再進行模板匹配的,且在替換完成後自動再加上這個首碼。這個功能對很多種類的重寫是非常重要的,因為如果沒有去首碼,則要進行父目錄的匹配,而父目錄的資訊並不是總能得到的。一個例外是,當substitution中有http://打頭時,則不再自動增加首碼了,如果P標誌出現,則會強制轉向代理。
注意:如果要在某個目錄範圍內啟動重寫引擎,則需要在相應的目錄設定檔中設定“RewriteEngine on”,且目錄的“Options FollowSymLinks”必須設定。如果管理員由於安全原因沒有開啟FollowSymLinks,則不能使用重寫引擎。