Apache Rewrite 大全php
Apache的rewrite模塊,提供了一個基於規則的重寫(rewrite,也許譯爲重構更爲合適)引擎,來實時重寫發送到Apache的請求URL。因功能極其強大,被稱爲URL重寫的「瑞士軍刀」。html
這個模塊使用一個基於正則表達式解析器開發的重寫引擎,根據web管理員定義的規則來實時(on the fly)重寫請求URL。它支持任意數目的重寫規則,以及附加到一條規則上的任意數目的規則條件,從而提供了一套很是靈活和功能強大的URL處理機制。URL處理操做的實施與否,依賴於各類各樣的條件檢查,如檢查服務器變量、環境變量、HTTP頭字段、時間戳的值,甚至外部數據庫的檢索結果。這個模塊能夠在服務器範圍內(http.conf)、目錄範圍內(.htaccess)或請求串(query-string)的一部分處理有關的URL。重寫的結果URL,能夠指向一個站內的處理程序、指向站外的重定向或者一個站內的代理。與靈活和功能強大相隨的是設置的複雜,別期望一天內弄明白整個模塊。(因此,這個學習筆記也分了幾部分:)
web
內部處理過程正則表達式
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)express
指令apache |
語法瀏覽器 |
默認值緩存 |
說明tomcat |
備註 |
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 |
設置日誌級別 |
表示沒有,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 |
定義重寫規則 |
具體說明參見文檔 |
今天學習重寫規則的語法。
RewriteRule
Syntax: RewriteRule Pattern Substitution [flags]
一條RewriteRule指令,定義一條重寫規則,規則間的順序很是重要。對Apache1.2及之後的版本,模板(pattern)是一個POSIX正則式,用以匹配當前的URL。當前的URL不必定是用記最初提交的URL,由於可能用一些規則在此規則前已經對URL進行了處理。
對mod_rewrite來講,!是個合法的模板前綴,表示「非」的意思,這對描述「不知足某種匹配條件」的狀況很是方便,或用做最後一條默認規則。當使用!時,不能在模板中有分組的通配符,也不能作後向引用。
當匹配成功後,Substitution會被用來替換相應的匹配,它除了能夠是普通的字符串之外,還能夠包括:
1. $N,引用RewriteRule模板中匹配的相關字串,N表示序號,N=0..9
2. %N,引用最後一個RewriteCond模板中匹配的數據,N表示序號
3. %{VARNAME},服務器變量
4. ${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是可選參數,當有多個標誌同時出現時,彼此間以逗號分隔。
1. '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標誌來中重寫流程。
2. 'forbidden|F' (強制禁止訪問URL所指的資源)
當即返回狀態值403 (FORBIDDEN)的應答包。將這個標誌與合適的RewriteConds 聯合使用,能夠阻斷訪問某些URL。
3. 'gone|G' (強制返回URL所指資源爲不存在(gone))
當即返回狀態值410 (GONE)的應答包。用這個標誌來標記URL所指的資源永久消失了.
4. # 'proxy|P' (強制將當前URL送往代理模塊(proxy module))
這個標誌,強制將substitution看成一個發向代理模塊的請求,並當即將共送往代理模塊。所以,必須確保substitution串是一個合法的URI (如, 典型的狀況是以http://hostname開頭),不然會從代理模塊獲得一個錯誤. 這個標誌,是ProxyPass指令的一個更強勁的實現,將遠程請求(remote stuff)映射到本地服務器的名字空間(namespace)中來。
注意,使用這個功能必須確保代理模塊已經編譯到Apache 服務器程序中了. 能夠用「httpd -l 」命令,來檢查輸出中是否含有mod_proxy.c來確認一下。若是沒有,而又須要使用這個功能,則須要從新編譯``httpd''程序並使用mod_proxy有效。
5. 'last|L' (最後一條規則)
停止重寫流程,再也不對當前URL施加更多的重寫規則。這至關於perl的last命令或C的break命令。
6. 'next|N' (下一輪)
從新從第一條重寫規則開始執行重寫過程,新開的過程當中的URL不該當與最初的URL相同。 這至關於Perl的next命令或C的continue命令. 千萬當心不要產生死循環。
7. # 'chain|C' (將當前的規則與其後續規則??綁(chained))
當規則匹配時,處理過程與沒有??綁同樣;若是規則不匹配,則??綁在一塊兒的後續規則也不在檢查和執行。
8. 'type|T=MIME-type' (強制MIME類型)
強制將目標文件的MIME-type爲某MIME類型。例如,這可用來模仿mod_alias模塊對某目錄的ScriptAlias指定,經過強制將該目錄下的全部文件的類型改成 「application/x-httpd-cgi」.
9. 'nosubreq|NS' (used only if no internal sub-request )
這個標誌強制重寫引擎跳過爲內部sub-request的重寫規則.例如,當mod_include試圖找到某一目錄下的默認文件時 (index.xxx),sub-requests 會在Apache內部發生. Sub-requests並不是老是有用的,在某些狀況下若是整個規則集施加到它上面,會產生錯誤。利用這個標誌可排除執行一些規則。
10. 'nocase|NC' (模板不區分大小寫)
這個標誌會使得模板匹配當前URL時忽略大小寫的差異。
11. 'qsappend|QSA' (追加請求串(query string))
這個標誌,強制重寫引擎爲Substitution的請求串追加一部分串,則不是替換掉原來的。藉助這個標誌,可使用一個重寫規則給請求串增長更多的數據。
12. 'noescape|NE' (不對輸出結果中的特殊字符進行轉義處理)
一般狀況下,mod_write的輸出結果中,特殊字符(如'%', '$', ';', 等)會轉義爲它們的16進制形式(如分別爲'%25', '%24', and '%3B')。這個標誌會禁止mod_rewrite對輸出結果進行此類操做。 這個標誌只能在 Apache 1.3.20及之後的版本中使用。
13. '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的使用。
14. 'skip|S=num' (跳事後面的num個規則)
當前規則匹配時,強制重寫引擎跳事後續的num個規則。用這個能夠來模仿if-then-else結構:then子句的最後一條rule的標誌是skip=N,而N是else子句的規則條數。
15. 'env|E=VAR:VAL' (設置環境變量)
設置名爲VAR的環境變量的值爲VAL,其中VAL中能夠含有正則式的後向引用($N或%N)。這個標誌可使用屢次,以設置多個環境變量。這兒設置的變量,能夠在多種狀況下被引用,如在XSSI或CGI中。另外,也能夠在RewriteCond模板中以%{ENV:VAR}的形式被引用。
16.
注意:必定不要忘記,在服務器範圍內的配置文件中,模板(pattern)用以匹配整個URL;而在目錄範圍內的配置文件中,目錄前綴老是被自動去掉後再進行模板匹配的,且在替換完成後自動再加上這個前綴。這個功能對不少種類的重寫是很是重要的,由於若是沒有去前綴,則要進行父目錄的匹配,而父目錄的信息並非總能獲得的。一個例外是,當substitution中有http://打頭時,則再也不自動增長前綴了,若是P標誌出現,則會強制轉向代理。
注意:若是要在某個目錄範圍內啓動重寫引擎,則須要在相應的目錄配置文件中設置「RewriteEngine on」,且目錄的「Options FollowSymLinks」必須設置。若是管理員因爲安全緣由沒有打開FollowSymLinks,則不能使用重寫引擎。
今天學習重寫規則的條件。
RewriteCond
Syntax: RewriteCond TestString CondPattern [flags]
RewriteCond指令定義一條規則條件。在一條RewriteRule指令前面可能會有一條或多條RewriteCond指令,只有當自身的模板(pattern)匹配成功且這些條件也知足時規則才被應用於當前URL處理。
TestString是一個字符串,除了包含普通的字符外,還能夠包括下列的可擴展結構:
1. $N,RewriteRule後向引用,其中(0 <= N <= 9)
$N引用緊跟在RewriteCond後面的RewriteRule中模板中的括號中的模板在當前URL中匹配的數據。
2. %N,RewriteCond後向引用,其中(0 <= N <= 9)
%N引用最後一個RewriteCond的模板中的括號中的模板在當前URL中匹配的數據。
3. ${mapname:key|default},RewriteMap擴展.
具體參見RewriteMap
4. %{ NAME_OF_VARIABLE } ,服務器變量。
變量的名字以下表(分類顯示)
HTTP headers: |
connection & request: |
server internals: |
system stuff: |
HTTP_USER_AGENT |
REMOTE_ADDR |
DOCUMENT_ROOT |
TIME_YEAR |
HTTP_REFERER |
REMOTE_HOST |
SERVER_ADMIN |
TIME_MON |
HTTP_COOKIE |
REMOTE_USER |
SERVER_NAME |
TIME_DAY |
HTTP_FORWARDED |
REMOTE_IDENT |
SERVER_ADDR |
TIME_HOUR |
HTTP_HOST |
REQUEST_METHOD |
SERVER_PORT |
TIME_MIN |
HTTP_PROXY_CONNECTION |
SCRIPT_FILENAME |
SERVER_PROTOCOL |
TIME_SEC |
HTTP_ACCEPT |
PATH_INFO |
SERVER_SOFTWARE |
TIME_WDAY |
|
QUERY_STRING |
|
TIME |
|
AUTH_TYPE |
|
|
5.
specials: |
說明 |
API_VERSION |
Apache與模塊間的接口的版本號 |
THE_REQUEST |
客戶端發送到來的HTTP請求行的整行信息,不含其它的頭字段信息,如("GET /index.html HTTP/1.1") |
REQUEST_URI |
HTTP請求行中請求的資源 |
REQUEST_FILENAME |
請求中對應的服務器本地文件系統中全路徑文件名 |
IS_SUBREQ |
根據是否爲SubRequest,分別值爲」true」或」false」 |
6.
特別說明:
o SCRIPT_FILENAME和REQUEST_FILENAME變量含有相同的值,也就是Apache服務器內部數據結構request_rec的filename字段的值。第一個變量是一個CGI變量,而第二個則與REQUEST_URI(含有request_rec數據結構中uri字段的值)保持一致。
o %{ENV:variable}中的variable能夠是任何環境變量的名字。對其值的查找,先經過Apache內部的數據結構,(如找不到)再在Apache服務器進程中經過getenv()查找。
o %{HTTP:header}中的header能夠是任何HTTP MIME-header的名字,其值經過查找HTTP請求信息而得。
o %{LA-U:variable} 用來引用後續API階段中定義的、當前還不知道的值,具體實現是經過執行一個基於URL的內部的sub-request來決定的variable的最終的值。例如,假如你想在服務器範圍內利用REMOTE_USER的值來完成重寫,但這個值是在驗證階段設置的,而驗證階段是在URL轉換階段的後面。從另外一方面講,因爲mod_rewrite在修補(fixup)API階段進行目錄範圍的重寫,而修補階段在驗證階段的後面,因此此時只要用%{REMOTE_USER}就能夠取得該值了。
o %{LA-F:variable},執行一個基於文件名字(filename)的內部sub-request來決定variable的最終的值。大多數時間內,這和LA-U相同。
CondPattern是一個條件模板,也就是說,是一個擴展正則式(extended regular expression),用與跟TestString進行匹配。做爲一個標準的擴展正則式,CondPattern有如下補充:
1. 能夠在模板串前增長一個!前綴,以用表示不匹配模板。但並非全部的test均可以加!前綴。
2. CondPattern中可使用如下特殊變量:
o ' 將condPattern看成一個普通字符串,將它和TestString進行比較,當TestString 的字符小於CondPattern爲真.
o '>CondPattern' (大於)
將condPattern看成一個普通字符串,將它和TestString進行比較,當TestString 的字符大於CondPattern爲真.
o '=CondPattern' (等於)
將condPattern看成一個普通字符串,將它和TestString進行比較,當TestString 與CondPattern徹底相同時爲真.若是CondPattern只是 "" (兩個引號緊挨在一塊兒) 此時需TestString爲空字符串方爲真.
o '-d' (是否爲目錄)
將testString看成一個目錄名,檢查它是否存在以及是不是一個目錄.
o '-f' (是不是regular file)
將testString看成一個文件名,檢查它是否存在以及是不是一個regular文件.
o '-s' (是否爲長度不爲的regular文件)
將testString看成一個文件名,檢查它是否存在以及是不是一個長度大於的regular文件
o '-l' (是否爲symbolic link)
將testString看成一個文件名,檢查它是否存在以及是不是一個 symbolic link.
o '-F' (經過subrequest來檢查某文件是否可訪問)
檢查TestString是不是一個合法的文件,並且經過服務器範圍內的當前設置的訪問控制進行訪問。這個檢查是經過一個內部subrequest完成的, 所以須要當心使用這個功能以下降服務器的性能。
o '-U' (經過subrequest來檢查某個URL是否存在)
檢查TestString是不是一個合法的URL,並且經過服務器範圍內的當前設置的訪問控制進行訪問。這個檢查是經過一個內部subrequest完成的, 所以須要當心使用這個功能以下降服務器的性能。
[flags]是第三個參數,多個標誌之間用逗號分隔。
1. 'nocase|NC' (不區分大小寫)
在擴展後的TestString和CondPattern中,比較時不區分文本的大小寫。注意,這個標誌對文件系統和subrequest檢查沒有影響.
2. 'ornext|OR' (創建與下一個條件的或的關係)
默認的狀況下,二個條件之間是AND的關係,用這個標誌將關係改成OR。例如:
RewriteCond %{REMOTE_HOST} ^host1.* [OR]
RewriteCond %{REMOTE_HOST} ^host2.* [OR]
RewriteCond %{REMOTE_HOST} ^host3.*
RewriteRule ...
若是沒有[OR]標誌,須要寫三個條件/規則.
例子:根據客戶端瀏覽器的不一樣,返回不一樣的首頁面。
RewriteCond %{HTTP_USER_AGENT} ^Mozilla.*
RewriteRule ^/$ /homepage.max.html [L]
RewriteCond %{HTTP_USER_AGENT} ^Lynx.*
RewriteRule ^/$ /homepage.min.html [L]
RewriteRule ^/$ /homepage.std.html [L]
今天學習重寫影射等內容。
RewriteMap
Syntax: RewriteMap MapName MapType:MapSource
RewriteMap指令定義一個重寫影射(Rewriting Map),在規則的substitution串中,經過影射函數(mapping-functions)來查找關鍵字(key),並用關鍵字對應的值來進行來行插入或替換操做。這個查找的對象,能夠是各類各樣的。
MapName是影射的名字,將用來經過下列的某種結構來爲substitution定義影射函數:
${ MapName : LookupKey }
${ MapName : LookupKey | DefaultValue }
當這些結構之一出現substitution串中時,重寫引擎會到mapname影射中查找lookupkey關鍵字,若是找到了就用返回的值(substvalue)來替換該結構,若是找不到就用defaultvalue來替換該結構,若是沒有defaultvalue,就用空串來替換。
MapType 和mapSource組合有如下幾種:
1. 標準的普通文本(Standard Plain Text)
MapType: txt, MapSource: Unix文件系統中合法的帶有路徑的regular file名
此種狀況下,MapSource文件是一個普通的ASCII文本文件,能夠含有空行、註釋行(以#打頭),及如下結構的鍵值對行(每一個鍵值對一行)。
MatchingKey SubstValue
例如:Mapsource文件叫/path/to/file/map.txt,其內容爲
##
## map.txt -- rewriting map
##
Ralf.S.Engelschall rse # Bastard Operator From Hell
Mr.Joe.Average joe # Mr. Average
在配置文件中則能夠這樣定義重寫映射:
RewriteMap real-to-user txt:/path/to/file/map.txt
2. 隨機的普通文本(Randomized Plain Text)
MapType: rnd, MapSource: Unix文件系統中合法的帶有路徑的regular file名
這種狀況與標準普通文本狀況很類似,差異只是在SubstValue的格式上:此時的SubstValue由一些用」|」分隔的值組成的串,這個「|」是「或者」的意思。當根據鍵值找到對應的SubstValue後,mod_rewrite藉助「|」將此串分解爲一些候選項,而後隨機選擇一項做爲最終的SubstValue的值返回。這聽起來有點瘋狂或毫無用處,其實這是設計用來在反向代理(reverse proxy)的狀況下作負載均衡,用來查找服務器的名字。
例如:MapSource文件的名字爲/path/to/file/map.txt,內容以下:
##
## map.txt -- rewriting map
##
static www1|www2|www3|www4
dynamic www5|www6
在配置文件中定義的重寫影射爲:
RewriteMap servers rnd:/path/to/file/map.txt
3. Hash File
MapType: dbm, MapSource: Unix文件系統中合法的帶有路徑的regular file名
這兒的Mapsource文件是一個二進制的NDBM格式的文件,含有與普通文本格式文件時相同的內容,爲了實現快速查找進行了優化處理後以一種特殊的格式來表達。 能夠用任何NDBM工具或者用下面的Perl腳本txt2dbm.pl來建立這種格式的文件:
#!/path/to/bin/perl
##
## txt2dbm -- convert txt map to dbm format
##
use NDBM_File;
use Fcntl;
($txtmap, $dbmmap) = @ARGV;
open(TXT, "<$txtmap") or die "Couldn't open $txtmap!\n";
tie (%DB, 'NDBM_File', $dbmmap,O_RDWR|O_TRUNC|O_CREAT, 0644) or die "Couldn't create $dbmmap!\n";
while () {
next if (/^\s*#/ or /^\s*$/);
$DB{$1} = $2 if (/^\s*(\S+)\s+(\S+)/);
}
untie %DB;
close(TXT);
此腳本的使用方法以下:
$ perl txt2dbm.pl map.txt map.db
4. Apache內部函數(Internal Function)
MapType: int, MapSource: Apache內部函數
這時,MapSource是一個Apache內置函數,目前還不能建立本身的內部函數,只能使用Apache已經定義好的:
o toupper
將鍵值轉換爲大寫
o tolower
將鍵值轉換爲小寫
o escape
將鍵值中的特殊字符轉換爲以16進製表示。
o unescape
將鍵值中16進製表示的特殊字符轉換爲原來的樣子。
5. 外部重寫程序(External Rewriting Program)
MapType: prg, MapSource: Unix文件系統中合法的帶有路徑的regular file名
這兒的MapSource是一個程序而不是一個影射文件。你可使用任何語言建立這個程序,但這個程序必須是可執行的(也就是說,要麼是二進制碼,要麼是首行帶有」#!/path/to/interpreter」格式的解釋器的可執行腳本)。
這個程序在Apache啓動時被運行,而後它就用它的標準輸入與標準輸出的文件句柄與重寫引擎通訊(暗指程序能無限時等待標準輸入,見下列)。對每一個影射函數的查找要求,它把其標準輸入中獲得的字符串(newline-terminated string?)做爲鍵值,而後經過標準輸出返回查找到的值,若是找不到相應的值,則返回以四字符的字符串」NULL「。下面一個簡單的例子,實現將鍵值做爲查找結果返回:
#!/usr/bin/perl
$| = 1;
while () {
# ...put here any transformations or lookups...
print $_;
}
注意:
1. ``Keep it simple, stupid'' (KISS),由於當規則執行時,一量這個外部程序掛起,整個Apache服務器就掛起了
2. 避免下述常見錯誤:緩存了標準輸出的I/O。這會致使死循環。這也是爲何上例中有``$|=1''這麼一行的緣由
3. 用RewriteLock指令定義一個lockfile,以同步與外部程序的通訊。默認狀況下並無這樣的同步
RewriteMap能夠出現不僅一次。每一個影射函數用RewriteMap來聲明它的影射文件. While you cannot declare a map in per-directory context it is of course possible to use this map in per-directory context.(?)對普通文本和DBM格式的文件,其鍵值被緩存在Apache內核中,直到影射文件的mtime 變化了或Apache重啓動了。此時,能夠將規則中的影射函數用於每一個請求。
RewriteBase
Syntax: RewriteBase URL-path
RewriteBase明確地指出目錄範圍內的重寫結果的baseURL.RewriteRule指令能夠用在目錄範圍內的配置文件裏(.htaccess)。在目錄範圍的重寫實施時,由本地路徑信息構成的前綴已經被去掉,重寫規則只對剩餘的部分進行處理。處理結束後,被去掉的路徑信息要自動加上,由於當對一個URL進行替換後,從新引擎須要將它從新插入到服務器的處理流程中去。若是服務器端的URL與文件的物理路徑有直接有關係,則每當在.htacess文中定義重寫規則時都須要用RewriteBase指URL-path。
雜項Environment Variables
mod_rewrite還維護着兩個非標準的CGI/SSI環境變量,名爲SCRIPT_URL和 SCRIPT_URI,存放着當前資源的邏輯web視圖(logical Web-view)。標準的CGI/SSI變量SCRIPT_NAME 和 SCRIPT_FILENAME中存放着當前資源的物理系統視圖(physical System-view)。請看例子:
SCRIPT_NAME=/sw/lib/w3s/tree/global/u/rse/.www/index.html
SCRIPT_FILENAME=/u/rse/.www/index.html
SCRIPT_URL=/u/rse/
SCRIPT_URI=http://en1.engelschall.com/u/rse/
今天主要列出一些例子。因爲有些例子是針對特殊路徑或特別狀況的,列出供你們在思路上參考。由於它們就是些例子。
目標 |
重寫設置 |
說明 |
規範化URL |
RewriteRule ^/~([^/]+)/?(.*) /u/$1/$2 [R] |
將/~user重寫爲/u/user的形式 |
|
RewriteRule ^/([uge])/([^/]+)$ /$1/$2/ [R] |
將/u/user末尾漏掉的/補上 |
|
|
|
規範化HostName |
RewriteCond %{HTTP_HOST} !^fully\.qualified\.domain\.name [NC] |
域名不合格 |
|
RewriteCond %{HTTP_HOST} !^$ |
不空 |
|
RewriteCond %{SERVER_PORT} !^80$ |
不是80端口 |
|
RewriteRule ^/(.*) http://fully.qualified.domain.name:%{SERVER_PORT}/$1 [L,R] |
重寫 |
|
RewriteCond %{HTTP_HOST} !^fully\.qualified\.domain\.name [NC] |
|
|
RewriteCond %{HTTP_HOST} !^$ |
|
|
RewriteRule ^/(.*) http://fully.qualified.domain.name/$1 [L,R] |
|
|
|
|
URL根目錄轉移 |
RewriteEngine on |
|
|
RewriteRule ^/$ /e/www/ [R] |
從/移到/e/www/ |
|
|
|
末尾目錄補斜線 |
RewriteEngine on |
|
(目錄範圍內) |
RewriteBase /~quux/ |
|
|
RewriteRule ^foo$ foo/ [R] |
/~quux/foo是一個目錄,補/ |
|
|
|
|
RewriteEngine on |
|
|
RewriteBase /~quux/ |
|
|
RewriteCond %{REQUEST_FILENAME} -d |
若是請文件名是個目錄 |
|
RewriteRule ^(.+[^/])$ $1/ [R] |
URL末尾不是斜線時補上 |
|
|
|
Web集羣 |
RewriteEngine on |
|
|
RewriteMap user-to-host txt:/path/to/map.user-to-host |
用戶-服務器映射 |
|
RewriteMap group-to-host txt:/path/to/map.group-to-host |
組-服務器映射 |
|
RewriteMap entity-to-host txt:/path/to/map.entity-to-host |
實體-服務器映射 |
|
RewriteRule ^/u/([^/]+)/?(.*) http://${user-to-host:$1|server0}/u/$1/$2 |
用戶均衡 |
|
RewriteRule ^/g/([^/]+)/?(.*) http://${group-to-host:$1|server0}/g/$1/$2 |
組均衡 |
|
RewriteRule ^/e/([^/]+)/?(.*) http://${entity-to-host:$1|server0}/e/$1/$2 |
實體均衡 |
|
RewriteRule ^/([uge])/([^/]+)/?$ /$1/$2/.www/ |
|
|
RewriteRule ^/([uge])/([^/]+)/([^.]+.+) /$1/$2/.www/$3\ |
|
|
|
|
URL根目錄搬遷 |
RewriteEngine on |
|
|
RewriteRule ^/~(.+) http://newserver/~$1 [R,L] |
到其它服務器 |
|
|
|
所用戶名首字母分 |
RewriteEngine on |
|
|
RewriteRule ^/~(([a-z])[a-z0-9]+)(.*) /home/$2/$1/.www$3 |
內一層括號爲$2 |
|
|
|
NCSA imagemap移 |
RewriteEngine on |
|
植爲mod_imap |
RewriteRule ^/cgi-bin/imagemap(.*) $1 [PT] |
|
|
|
|
多目錄查找資源 |
RewriteEngine on |
|
|
# first try to find it in custom/... |
|
|
RewriteCond /your/docroot/dir1/%{REQUEST_FILENAME} -f |
|
|
RewriteRule ^(.+) /your/docroot/dir1/$1 [L] |
|
|
# second try to find it in pub/... |
|
|
RewriteCond /your/docroot/dir2/%{REQUEST_FILENAME} -f |
|
|
RewriteRule ^(.+) /your/docroot/dir2/$1 [L] |
|
|
# else go on for other Alias or ScriptAlias directives, |
|
|
RewriteRule ^(.+) - [PT] |
|
|
|
|
據URL設置環境變量 |
RewriteEngine on |
|
|
RewriteRule ^(.*)/S=([^/]+)/(.*) $1/$3 [E=STATUS:$2] |
|
|
|
|
虛擬主機 |
RewriteEngine on |
|
|
RewriteCond %{HTTP_HOST} ^www\.[^.]+\.host\.com$ |
基於用戶名 |
|
RewriteRule ^(.+) %{HTTP_HOST}$1 [C] |
|
|
RewriteRule ^www\.([^.]+)\.host\.com(.*) /home/$1$2 |
|
|
|
|
內外人有別 |
RewriteEngine on |
|
|
RewriteCond %{REMOTE_HOST} !^.+\.ourdomain\.com$ |
基於遠程主機 |
|
RewriteRule ^(/~.+) http://www.somewhere.com/$1 [R,L] |
|
|
|
|
錯誤重定向 |
RewriteEngine on |
|
|
RewriteCond /your/docroot/%{REQUEST_FILENAME} !-f |
不是regular文件 |
|
RewriteRule ^(.+) http://webserverB.dom/$1 |
|
|
|
|
程序處理特殊協議 |
RewriteRule ^xredirect:(.+) /path/to/nph-xredirect.cgi/$1 \ |
Xredirect協議 |
|
[T=application/x-httpd-cgi,L] |
|
|
|
|
最近鏡像下載 |
RewriteEngine on |
|
|
RewriteMap multiplex txt:/path/to/map.cxan |
頂級域名與最近ftp服務器映射 |
|
RewriteRule ^/CxAN/(.*) %{REMOTE_HOST}::$1 [C] |
|
|
RewriteRule ^.+\.([a-zA-Z]+)::(.*)$ ${multiplex:$1|ftp.default.dom}$2 [R,L] |
據頂級域名不一樣提供不一樣的FTP服務器 |
|
|
|
基於時間重寫 |
RewriteEngine on |
|
|
RewriteCond %{TIME_HOUR}%{TIME_MIN} >0700 |
|
|
RewriteCond %{TIME_HOUR}%{TIME_MIN} <1900 |
|
|
RewriteRule ^foo\.html$ foo.day.html |
白天爲遲早7點間 |
|
RewriteRule ^foo\.html$ foo.night.html |
其他爲夜間 |
|
|
|
向前兼容擴展名 |
RewriteEngine on |
|
|
RewriteBase /~quux/ |
|
|
# parse out basename, but remember the fact |
|
|
RewriteRule ^(.*)\.html$ $1 [C,E=WasHTML:yes] |
|
|
# rewrite to document.phtml if exists |
|
|
RewriteCond %{REQUEST_FILENAME}.phtml -f |
若是存在$1.phtml則重寫 |
|
RewriteRule ^(.*)$ $1.phtml [S=1] |
|
|
# else reverse the previous basename cutout |
|
|
RewriteCond %{ENV:WasHTML} ^yes$ |
若是不存在$1.phtml,則保持不變 |
|
RewriteRule ^(.*)$ $1.html |
|
|
|
|
文件更名(目錄級) |
RewriteEngine on |
內部重寫 |
|
RewriteBase /~quux/ |
|
|
RewriteRule ^foo\.html$ bar.html |
|
|
|
|
|
RewriteEngine on |
重定向由客戶端再次提交 |
|
RewriteBase /~quux/ |
|
|
RewriteRule ^foo\.html$ bar.html [R] |
|
|
|
|
據瀏覽器類型重寫 |
RewriteCond %{HTTP_USER_AGENT} ^Mozilla/3.* |
|
|
RewriteRule ^foo\.html$ foo.NS.html [L] |
|
|
RewriteCond %{HTTP_USER_AGENT} ^Lynx/.* [OR] |
|
|
RewriteCond %{HTTP_USER_AGENT} ^Mozilla/[12].* |
|
|
RewriteRule ^foo\.html$ foo.20.html [L] |
|
|
RewriteRule ^foo\.html$ foo.32.html [L] |
|
|
|
|
動態鏡像遠程資源 |
RewriteEngine on |
|
|
RewriteBase /~quux/ |
|
|
RewriteRule ^hotsheet/(.*)$ http://www.tstimpreso.com/hotsheet/$1 [P] |
利用了代理模塊 |
|
|
|
|
RewriteEngine on |
|
|
RewriteBase /~quux/ |
|
|
RewriteRule ^usa-news\.html$ http://www.quux-corp.com/news/index.html [P] |
|
|
|
|
反向動態鏡像 |
RewriteEngine on |
|
|
RewriteCond /mirror/of/remotesite/$1 -U |
|
|
RewriteRule ^http://www\.remotesite\.com/(.*)$ /mirror/of/remotesite/$1 |
|
|
|
|
負載均衡 |
RewriteEngine on |
利用代理實現round-robin效果 |
|
RewriteMap lb prg:/path/to/lb.pl |
|
|
RewriteRule ^/(.+)$ ${lb:$1} [P,L] |
|
|
|
|
|
#!/path/to/perl |
|
|
$| = 1; |
|
|
$name = "www"; # the hostname base |
|
|
$first = 1; # the first server (not 0 here, because 0 is myself) |
|
|
$last = 5; # the last server in the round-robin |
|
|
$domain = "foo.dom"; # the domainname |
|
|
$cnt = 0; |
|
|
while (<STDIN>) { |
|
|
$cnt = (($cnt+1) % ($last+1-$first)); |
|
|
$server = sprintf("%s%d.%s", $name, $cnt+$first, $domain); |
|
|
print "http://$server/$_"; |
|
|
} |
|
|
##EOF## |
|
|
|
|
靜態頁面變腳本 |
RewriteEngine on |
|
|
RewriteBase /~quux/ |
|
|
RewriteRule ^foo\.html$ foo.cgi [T=application/x-httpd-cgi] |
|
|
|
|
阻擊機器人 |
RewriteCond %{HTTP_USER_AGENT} ^NameOfBadRobot.* |
|
|
RewriteCond %{REMOTE_ADDR} ^123\.45\.67\.[8-9]$ |
|
|
RewriteRule ^/~quux/foo/arc/.+ - [F] |
|
|
|
|
阻止盜連你的圖片 |
RewriteCond %{HTTP_REFERER} !^$ |
|
|
RewriteCond %{HTTP_REFERER} !^http://www.quux-corp.de/~quux/.*$ [NC] |
本身的鏈接可不能被阻止 |
|
RewriteRule .*\.gif$ - [F] |
|
|
|
|
|
RewriteCond %{HTTP_REFERER} !^$ |
|
|
RewriteCond %{HTTP_REFERER} !.*/foo-with-gif\.html$ |
|
|
RewriteRule ^inlined-in-foo\.gif$ - [F] |
|
|
|
|
拒絕某些主機訪問 |
RewriteEngine on |
|
|
RewriteMap hosts-deny txt:/path/to/hosts.deny |
|
|
RewriteCond ${hosts-deny:%{REMOTE_HOST}|NOT-FOUND} !=NOT-FOUND [OR] |
|
|
RewriteCond ${hosts-deny:%{REMOTE_ADDR}|NOT-FOUND} !=NOT-FOUND |
|
|
RewriteRule ^/.* - [F] |
|
|
|
|
用戶受權 |
RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST} !^friend1@client1.quux-corp\.com$ |
|
|
RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST} !^friend2@client2.quux-corp\.com$ |
|
|
RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST} !^friend3@client3.quux-corp\.com$ |
|
|
RewriteRule ^/~quux/only-for-friends/ - [F] |
|
|
|
|
外部重寫程序模板 |
RewriteEngine on |
|
|
RewriteMap quux-map prg:/path/to/map.quux.pl |
|
|
RewriteRule ^/~quux/(.*)$ /~quux/${quux-map:$1} |
|
|
|
|
|
#!/path/to/perl |
|
|
$| = 1; |
|
|
while (<>) { |
|
|
s|^foo/|bar/|; |
|
|
print $_; |
|
|
} |
|
|
|
|
搜索引擎友好 |
RewriteRule ^/products$ /content.php |
|
|
RewriteRule ^/products/([0-9]+)$ /content.php?id=$1 |
|
|
RewriteRule ^/products/([0-9]+),([ad]*),([0-9]{0,3}),([0-9]*),([0-9]*$) /marso/content.php?id=$1&sort=$2&order=$3&start=$4 |
|
狗尾續貂一把,把我在實際中用的apache+mod_jk+tomcat+mod_rewrite來重寫含有.jsp的URL設置帖出來,其實就是簡單地把前面學的用了一下,供參考。
假定你已經利用mod_jk把apache和tomcat協同起來工做了,而且已經開發了一個web應用叫mywebapp。對httpd.conf改動以下:
1. 兩個模塊的加載順序
...
LoadModule rewrite_module modules/mod_rewrite.so
...
LoadModule jk_module modules/mod_jk.so
...
AddModule mod_jk.c
....
AddModule mod_rewrite.c
....
2. 兩個模塊的設置順序
...
<IfModule mod_jk.c>
...
JkWorkersFile "/path/to/workers.properties"
JkLogFile "/path/to/mod_jk.log"
JkLogLevel info
...
JkMount /*.jsp ajp13
</IfModule>
...
<VirtualHost *>
...
ServerName hedong.3322.org
...
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteMap id2jsp txt:/path/to/id2jsp.txt #jsp文件名編碼表
RewriteMap id2db txt:/path/to/id2db.txt #數據庫名字編碼表
RewriteRule ^/mywebapp/([0-9]*)/([0-9]*),([0-9]*)\.html ${id2jsp:$1}\?dbname=${id2db:$2}&r=$3 [PT] #參數r表示記錄ID,PT容許mod_jk繼續處理請求URL
RewriteRule ^/mywebapp$ /mywebapp/ [R] #被全目錄名後被省略的/
RewriteRule ^/mywebapp/$ /mywebapp/index\.jsp [PT] #PT容許mod_jk繼續處理請求URL
....
</IfModule>
JkMount /mywebapp ajp13
JkMount /mywebapp/* ajp13
....
</VirtualHost>
3. 一點感想
當用mod_rewrite在服務器範圍內對請求URL進行重寫時,若是涉及到URL中目錄層次的變化(如原本是一層目錄/a/page.html,如今被寫成/jsps/part1/page.jsp)時,要當心,由於:HTML/jsp頁面中不少連接是相對連接,尤爲是圖片連接尤其常見, 即如../images/1.jpg等。
當你重寫了URL的目錄層次時,頁面中的相對連接不受重寫的影響,而致出現顯示問題。
接着上面的例子,假定/jsps/part1/page.jsp中有一個連接爲../images/1.jpg,並且/jsps/images/1.jpg確實存在,當不用mod_rewrite重寫時,/jsps/part1/page.jsp顯示正常。而重寫之後,page.jsp能運行,而1.jpg的URL變成了/images/1.jpg了,雖然你在重寫是改動了目錄,此1.jpg的URL仍是相對於/a/的。