Apache HTTP Server 版本2.2
Originally written by
Ralf S. Engelschall <rse@apache.org>
December 1997
本文是mod_rewrite
的參考文檔,闡述在實(shí)際應(yīng)用中如何解決網(wǎng)管所面臨的基于URL的典型問題,并詳細(xì)描述了如何配置URL重寫規(guī)則集以解決問題。
mod_rewrite
簡介Apache的mod_rewrite
是提供了強(qiáng)大URL操作的殺手級的模塊,可以實(shí)現(xiàn)幾乎所有你夢想的URL操作類型,其代價是你必須接受其復(fù)雜性,因?yàn)?code class="module">mod_rewrite的主要障礙就是初學(xué)者不容易理解和運(yùn)用,即使是Apache專家有時也會發(fā)掘出mod_rewrite
的新用途。換句話說:對于mod_rewrite
,或者是打退堂鼓永不再用,或者是喜歡它并一生受用。本文試圖通過對已有方案的表述來創(chuàng)造一個成功的開端,以免你放棄。
我自己創(chuàng)造和收集了許多實(shí)踐方案,不要有畏懼心理,從這些例子開始學(xué)習(xí)URL重寫的黑匣子吧。
mod_alias
和mod_userdir
時要增加[PT]標(biāo)志,或者重寫.htaccess而不是單個服務(wù)器中的規(guī)則集。對一個特定的規(guī)則集應(yīng)該先透徹理解然后再考慮應(yīng)用,這樣才能避免出現(xiàn)問題。在有些web服務(wù)器上,一個資源會擁有多個URL,在實(shí)際應(yīng)用和發(fā)布中應(yīng)該被使用的是規(guī)范的URL,其他的則是簡寫或者只在內(nèi)部使用。無論用戶在請求中使用什么形式的URL,他最終看見的都應(yīng)該是規(guī)范的URL。
對所有不規(guī)范的URL執(zhí)行一個外部HTTP重定向,以改變它在瀏覽器地址欄中的顯示及其后繼請求。下例中的規(guī)則集用規(guī)范的/u/user替換/~user,并修正了/u/user所遺漏的后綴斜杠。
RewriteRule ^/~([^/]+)/?(.*) /u/$1/$2 [R] RewriteRule ^/([uge])/([^/]+)$ /$1/$2/ [R]
# 針對運(yùn)行在非80端口的站點(diǎn) RewriteCond %{HTTP_HOST} !^fully\.qualified\.domain\.name [NC] RewriteCond %{HTTP_HOST} !^$ RewriteCond %{SERVER_PORT} !^80$ RewriteRule ^/(.*) http://fully.qualified.domain.name:%{SERVER_PORT}/$1 [L,R] # 對一個運(yùn)行在80端口的站點(diǎn) RewriteCond %{HTTP_HOST} !^fully\.qualified\.domain\.name [NC] RewriteCond %{HTTP_HOST} !^$ RewriteRule ^/(.*) http://fully.qualified.domain.name/$1 [L,R]
DocumentRoot
通常,web服務(wù)器的DocumentRoot
直接對應(yīng)于URL"/",但是,它常常不是處于最高一級,而可能只是眾多數(shù)據(jù)池中的一個實(shí)體。比如,在Intranet站點(diǎn)中,有/e/www/(WWW的主頁)、/e/sww/(Intranet的主頁)等等,而DocumentRoot
指向了/e/www/,則必須保證此數(shù)據(jù)池中所有內(nèi)嵌的圖片和其他元素對后繼請求有效。
只須重定向URL"/"到"/e/www/"即可。這個方案看起來很簡單,但只是因?yàn)橛辛薽od_rewrite模塊的支持,它才簡單,因?yàn)閭鹘y(tǒng)的URL Aliases機(jī)制(由mod_alias及其相關(guān)模塊提供)只是作了一個前綴匹配,DocumentRoot是一個對所有URL的前綴,因而無法實(shí)現(xiàn)這樣的重定向。而用mod_rewrite的確很簡單:
RewriteEngine on RewriteRule ^/$ /e/www/ [R]
注意, 也可以通過RedirectMatch
指令達(dá)到這個目的:
RedirectMatch ^/$ http://example.com/e/www/
每個網(wǎng)管對引用目錄后綴斜杠的問題都有一本苦經(jīng),如果遺漏了,服務(wù)器會產(chǎn)生一個錯誤,因?yàn)槿绻埱笫?~quux/foo而不是/~quux/foo/ ,服務(wù)器就會去找一個叫foo的文件,而它是一個目錄,所以就報錯了。事實(shí)上,大多數(shù)情況下,它自己會試圖修正這個錯誤,但是有時候需要你手工糾正,比如,在重寫了許多CGI腳本中的復(fù)雜的URL以后。
解決這個微妙問題的方案是讓服務(wù)器自動添加后綴斜杠。對此,必須使用一個外部重定向,使瀏覽器正確地處理后繼的對諸如圖片的請求。如果僅僅作一個內(nèi)部重寫,可能只對目錄頁面有效,而對內(nèi)嵌有使用相對URL的圖片的頁面無效,因?yàn)闉g覽器有請求內(nèi)嵌目標(biāo)的可能。比如,如果不用外部重定向,/~quux/foo/index.html頁面中對image.gif的請求,其結(jié)果將是/~quux/image.gif
所以,應(yīng)該這樣寫:
RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo$ foo/ [R]
又懶又瘋狂的做法是把這些寫入其宿主目錄中的頂級.htaccess中,但是須注意,如此會帶來一些處理上的開銷。
RewriteEngine on RewriteBase /~quux/ RewriteCond %{REQUEST_FILENAME} -d RewriteRule ^(.+[^/])$ $1/ [R]
我們希望在一個Intranet集群網(wǎng)站中,對所有WWW服務(wù)器建立一致的URL規(guī)劃,也就是說,所有的URL(針對每個服務(wù)器進(jìn)行本地配置,因此是獨(dú)立于各個服務(wù)器的)實(shí)際上都是獨(dú)立于各個服務(wù)器的!我們需要的是一個具有獨(dú)立于各個服務(wù)器的一致性規(guī)劃的WWW名稱空間,即URL不需要包含物理目標(biāo)服務(wù)器,而由集群本身來自動定位物理目標(biāo)主機(jī)。
首先,目標(biāo)服務(wù)器的信息來自(產(chǎn)生)于包含有用戶、組以及實(shí)體的外部地圖,其格式形如:
user1 server_of_user1 user2 server_of_user2 : :
這些信息被存入map.xxx-to-host文件。其次,如果URL在一個服務(wù)器上無效,需要引導(dǎo)所有的服務(wù)器重定向URL
/u/user/anypath /g/group/anypath /e/entity/anypath
到
http://physical-host/u/user/anypath http://physical-host/g/group/anypath http://physical-host/e/entity/anypath
以下規(guī)則集依靠映射文件來完成這個操作(假定,如果一個用戶在映射中沒有對應(yīng)的項(xiàng),則使用server0為默認(rèn)服務(wù)器):
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\
通常,許多網(wǎng)管在建立一個新的web服務(wù)器時,都會有這樣的要求:重定向一個web服務(wù)器上的所有用戶主目錄到另一個web服務(wù)器。
很簡單,在老的web服務(wù)器上重定向所有的URL"/~user/anypath"到http://newserver/~user/anypath
RewriteEngine on RewriteRule ^/~(.+) http://newserver/~$1 [R,L]
一些擁有幾千個用戶的網(wǎng)站通常都使用結(jié)構(gòu)化的用戶主目錄規(guī)劃,即每個用戶主目錄位于一個帶有特定前綴,比如其用戶名的第一個字符的子目錄下:/~foo/anypath代表/home/f/foo/.www/anypath,而/~bar/anypath代表/home/b/bar/.www/anypath
可以使用下列規(guī)則集來擴(kuò)展~以達(dá)到上述目的。
RewriteEngine on RewriteRule ^/~(([a-z])[a-z0-9]+)(.*) /home/$2/$1/.www$3
這是一個不加雕琢的例子:一個大量使用針對目錄的規(guī)則集以實(shí)現(xiàn)平滑的觀感,并且從來不用調(diào)整數(shù)據(jù)結(jié)構(gòu)的殺手級的應(yīng)用。背景:net.sw從1992年開始,存放了我收集的免費(fèi)Unix軟件包。它是我的愛好也是我的工作,因?yàn)樵趯W(xué)習(xí)計算機(jī)科學(xué)的同時,業(yè)余時間還做了多年的系統(tǒng)和網(wǎng)絡(luò)管理員。每周我都需要整理軟件,因而建立了一個層次很深的目錄結(jié)構(gòu)來存放各種軟件包:
drwxrwxr-x 2 netsw users 512 Aug 3 18:39 Audio/ drwxrwxr-x 2 netsw users 512 Jul 9 14:37 Benchmark/ drwxrwxr-x 12 netsw users 512 Jul 9 00:34 Crypto/ drwxrwxr-x 5 netsw users 512 Jul 9 00:41 Database/ drwxrwxr-x 4 netsw users 512 Jul 30 19:25 Dicts/ drwxrwxr-x 10 netsw users 512 Jul 9 01:54 Graphic/ drwxrwxr-x 5 netsw users 512 Jul 9 01:58 Hackers/ drwxrwxr-x 8 netsw users 512 Jul 9 03:19 InfoSys/ drwxrwxr-x 3 netsw users 512 Jul 9 03:21 Math/ drwxrwxr-x 3 netsw users 512 Jul 9 03:24 Misc/ drwxrwxr-x 9 netsw users 512 Aug 1 16:33 Network/ drwxrwxr-x 2 netsw users 512 Jul 9 05:53 Office/ drwxrwxr-x 7 netsw users 512 Jul 9 09:24 SoftEng/ drwxrwxr-x 7 netsw users 512 Jul 9 12:17 System/ drwxrwxr-x 12 netsw users 512 Aug 3 20:15 Typesetting/ drwxrwxr-x 10 netsw users 512 Jul 9 14:08 X11/
1996年7月,我決定通過一個漂亮的Web接口公開我的收藏。"漂亮"是指提供一個接口以直接瀏覽整個目錄結(jié)構(gòu),同時不對這個結(jié)構(gòu)做任何改變,甚至也不在結(jié)構(gòu)頂部放置CGI腳本。為什么呢?因?yàn)檫@個結(jié)構(gòu)還要能夠被FTP訪問,而且我不希望其中有任何Web或者CGI成分。
這個方案分為兩個部分:第一個部分,是用于在空閑時間建立所有目錄頁面的CGI腳本集。我把它們放在/e/netsw/.www/,如下:
-rw-r--r-- 1 netsw users 1318 Aug 1 18:10 .wwwacl drwxr-xr-x 18 netsw users 512 Aug 5 15:51 DATA/ -rw-rw-rw- 1 netsw users 372982 Aug 5 16:35 LOGFILE -rw-r--r-- 1 netsw users 659 Aug 4 09:27 TODO -rw-r--r-- 1 netsw users 5697 Aug 1 18:01 netsw-about.html -rwxr-xr-x 1 netsw users 579 Aug 2 10:33 netsw-access.pl -rwxr-xr-x 1 netsw users 1532 Aug 1 17:35 netsw-changes.cgi -rwxr-xr-x 1 netsw users 2866 Aug 5 14:49 netsw-home.cgi drwxr-xr-x 2 netsw users 512 Jul 8 23:47 netsw-img/ -rwxr-xr-x 1 netsw users 24050 Aug 5 15:49 netsw-lsdir.cgi -rwxr-xr-x 1 netsw users 1589 Aug 3 18:43 netsw-search.cgi -rwxr-xr-x 1 netsw users 1885 Aug 1 17:41 netsw-tree.cgi -rw-r--r-- 1 netsw users 234 Jul 30 16:35 netsw-unlimit.lst
其中的"DATA"子目錄包含了上述目錄結(jié)構(gòu),即實(shí)在的net.sw ,由rdist在需要的時候自動更新。第二個部分的遺留問題是:如何連接這兩個結(jié)構(gòu)為一個平滑觀感的URL樹?我希望在運(yùn)行適當(dāng)?shù)腃GI腳本而使用各種URL的時候,使用戶感覺不到"DATA"目錄的存在。方案如下:首先,我把下列配置放在服務(wù)器上DocumentRoot中針對目錄的配置文件里,重寫公布的URL"/net.sw/"為內(nèi)部路徑"/e/netsw" :
RewriteRule ^net.sw$ net.sw/ [R] RewriteRule ^net.sw/(.*)$ e/netsw/$1
第一條規(guī)則是針對遺漏后綴斜杠的請求的!第二條規(guī)則才是真正實(shí)現(xiàn)功能的。接著,就是放在針對目錄的配置文件/e/netsw/.www/.wwwacl中的殺手級的配置了:
Options ExecCGI FollowSymLinks Includes MultiViews RewriteEngine on # 我們通過"/net.sw/"前綴到達(dá) RewriteBase /net.sw/ # 首先重寫根目錄到cgi處理腳本 RewriteRule ^$ netsw-home.cgi [L] RewriteRule ^index\.html$ netsw-home.cgi [L] # 當(dāng)瀏覽器請求perdir頁面時剝?nèi)プ幽夸? RewriteRule ^.+/(netsw-[^/]+/.+)$ $1 [L] # 現(xiàn)在打斷本地文件的重寫 RewriteRule ^netsw-home\.cgi.* - [L] RewriteRule ^netsw-changes\.cgi.* - [L] RewriteRule ^netsw-search\.cgi.* - [L] RewriteRule ^netsw-tree\.cgi$ - [L] RewriteRule ^netsw-about\.html$ - [L] RewriteRule ^netsw-img/.*$ - [L] # 任何別的東西都是一個由另一個cgi腳本處理的子目錄 RewriteRule !^netsw-lsdir\.cgi.* - [C] RewriteRule (.*) netsw-lsdir.cgi/$1
閱讀提示:
mod_imap
許多人都希望在從NCSA web服務(wù)器向較現(xiàn)代的Apache web服務(wù)器轉(zhuǎn)移中實(shí)現(xiàn)平滑過渡,即希望老的NCSA圖像映射程序能在Apache的較現(xiàn)代的mod_imap
支持下正常運(yùn)作。但問題在于,到處都是通過/cgi-bin/imagemap/path/to/page.map引用imagemap程序的連接,而在Apache下,應(yīng)該寫成/path/to/page.map
使用全局規(guī)則在傳輸過程中去除所有這些請求的前綴:
RewriteEngine on RewriteRule ^/cgi-bin/imagemap(.*) $1 [PT]
有時會有必要使web服務(wù)器在多個目錄中搜索頁面,對此,MultiViews或者其他技術(shù)無能為力。
編制一個明確的規(guī)則集以搜索目錄中的文件。
RewriteEngine on # 首先嘗試在 custom/...中尋找 RewriteCond /your/docroot/dir1/%{REQUEST_FILENAME} -f RewriteRule ^(.+) /your/docroot/dir1/$1 [L] # 然后嘗試在 pub/...中尋找 RewriteCond /your/docroot/dir2/%{REQUEST_FILENAME} -f RewriteRule ^(.+) /your/docroot/dir2/$1 [L] # 再找不到就繼續(xù)尋找其他的Alias 或 ScriptAlias 目錄... RewriteRule ^(.+) - [PT]
如果希望保持請求之間的狀態(tài)信息,又不希望使用CGI來包裝所有頁面,而是只通過分離URL中的有用信息來編碼。
可以用一個規(guī)則集來分離出狀態(tài)信息,并設(shè)置環(huán)境變量以備此后用于XSSI或CGI 。這樣,一個"/foo/S=java/bar/"的URL會被解析為/foo/bar/ ,而環(huán)境變量STATUS則被設(shè)置為"java"。
RewriteEngine on RewriteRule ^(.*)/S=([^/]+)/(.*) $1/$3 [E=STATUS:$2]
如果需要為用戶username支持一個www.username.host.domain.com的主頁,但不是用在此機(jī)器上建虛擬主機(jī)的方法,而是用僅在此機(jī)器上增加一個DNS記錄的方法實(shí)現(xiàn)。
對HTTP/1.0的請求,這是無法實(shí)現(xiàn)的;但是對HTTP/1.1的在HTTP頭中包含有主機(jī)名的請求,可以用以下規(guī)則集來內(nèi)部地重寫http://www.username.host.com/anypath為/home/username/anypath
RewriteEngine on RewriteCond %{HTTP_HOST} ^www\.[^.]+\.host\.com$ RewriteRule ^(.+) %{HTTP_HOST}$1 [C] RewriteRule ^www\.([^.]+)\.host\.com(.*) /home/$1$2
對不是來自本地域ourdomain.com的外來訪問者的請求,重定向其用戶主目錄URL到另一個web服務(wù)器www.somewhere.com ,有時這種做法也會用在虛擬主機(jī)的配置段中。
只須一個重寫條件:
RewriteEngine on RewriteCond %{REMOTE_HOST} !^.+\.ourdomain\.com$ RewriteRule ^(/~.+) http://www.somewhere.com/$1 [R,L]
如何重寫URL以重定向?qū)eb服務(wù)器A的失敗請求到服務(wù)器B,是一個常見的問題。一般,可以用Perl寫的CGI腳本通過ErrorDocument
來解決,此外,還有mod_rewrite
方案。但是須注意,這種方法的執(zhí)行效率不如用ErrorDocument
的CGI腳本!
第一種方案,有最好的性能而靈活性欠佳,出錯概率小所以安全:
RewriteEngine on RewriteCond /your/docroot/%{REQUEST_FILENAME} !-f RewriteRule ^(.+) http://webserverB.dom/$1
但是其問題在于,它只對位于DocumentRoot
中的頁面有效。雖然可以增加更多的條件(比如同時還處理用戶主目錄,等等),但是還有一個更好的方法:
RewriteEngine on RewriteCond %{REQUEST_URI} !-U RewriteRule ^(.+) http://webserverB.dom/$1
這種方法使用了mod_rewrite
提供的"向前參照"(look-ahead)的功能,是一種對所有URL類型都有效而且安全的方法。但是,對web服務(wù)器的性能會有影響,所以如果web服務(wù)器有一個強(qiáng)大的CPU,那就用這個方法。而在慢速機(jī)器上,可以用第一種方法,或者用性能更好的ErrorDocument
CGI腳本。
有時候,我們會需要更多的對重定向URL的(有關(guān)字符轉(zhuǎn)義機(jī)制方面的)控制。通常,Apache內(nèi)核中的URL轉(zhuǎn)義函數(shù)uri_escape()同時還會對錨(anchor)轉(zhuǎn)義,即類似"url#anchor"的URL,因此,你不能用mod_rewrite
對此類URL直接重定向。那么如何實(shí)現(xiàn)呢?
必須用NPH-CGI腳本使它自己重定向,因?yàn)閷PH(無須解析的HTTP頭)不會發(fā)生轉(zhuǎn)義操作。首先,在針對服務(wù)器的配置中(應(yīng)該位于所有重寫規(guī)則的最后),引入一種新的URL類型"xredirect:":
RewriteRule ^xredirect:(.+) /path/to/nph-xredirect.cgi/$1 \ [T=application/x-httpd-cgi,L]
以強(qiáng)制所有帶"xredirect:"前綴的URL被傳送到如下的nph-xredirect.cgi程序:
#!/path/to/perl ## ## nph-xredirect.cgi -- NPH/CGI script for extended redirects ## $| = 1; $url = $ENV{'PATH_INFO'}; print "HTTP/1.0 302 Moved Temporarily\n"; print "Server: $ENV{'SERVER_SOFTWARE'}\n"; print "Location: $url\n"; print "Content-type: text/html\n"; print "\n"; print "<html>\n"; print "<head>\n"; print "<title>302 Moved Temporarily (EXTENDED)</title>\n"; print "</head>\n"; print "<body>\n"; print "<h1>Moved Temporarily (EXTENDED)</h1>\n"; print "The document has moved <a HREF=\"$url\">here</a>.<p>\n"; print "</body>\n"; print "</html>\n"; ##EOF##
這是一種可以重定向所有URL類型的方法,包括不被mod_rewrite
直接支持的類型。所以,還可以這樣重定向"news:newsgroup":
RewriteRule ^anyurl xredirect:news:newsgroup
你知道http://www.perl.com/CPAN的CPAN(綜合Perl存檔網(wǎng)絡(luò))?它實(shí)現(xiàn)了一個重定向以提供全世界的CPAN鏡像中離訪問者最近的一個FTP站點(diǎn),也可以稱之為FTP訪問多路復(fù)用服務(wù)。CPAN是通過CGI腳本實(shí)現(xiàn)的,那么用mod_rewrite
如何實(shí)現(xiàn)呢?
首先,我們注意到mod_rewrite
從3.0.0版本開始,還可以重寫"ftp:"類型。其次,對客戶端頂級域名的路徑最近的求取可以用RewriteMap
實(shí)現(xiàn)。利用鏈?zhǔn)揭?guī)則集,并用頂級域名作為查找多路復(fù)用地圖的鍵,可以這樣做:
RewriteEngine on RewriteMap multiplex txt:/path/to/map.cxan RewriteRule ^/CxAN/(.*) %{REMOTE_HOST}::$1 [C] RewriteRule ^.+\.([a-zA-Z]+)::(.*)$ ${multiplex:$1|ftp.default.dom}$2 [R,L]
## ## map.cxan -- Multiplexing Map for CxAN ## de ftp://ftp.cxan.de/CxAN/ uk ftp://ftp.cxan.uk/CxAN/ com ftp://ftp.cxan.com/CxAN/ : ##EOF##
在頁面內(nèi)容按時間不同而變化的場合,比如重定向特定頁面,許多網(wǎng)管仍然采用CGI腳本的方法,如何用mod_rewrite
來實(shí)現(xiàn)呢?
有許多類似TIME_xxx的變量可以用在重寫條件中,利用"<STRING", " >STRING"和"=STRING"的類型比較,并加以連接,就可以實(shí)現(xiàn)依賴于時間的重寫:
RewriteEngine on RewriteCond %{TIME_HOUR}%{TIME_MIN} >0700 RewriteCond %{TIME_HOUR}%{TIME_MIN} <1900 RewriteRule ^foo\.html$ foo.day.html RewriteRule ^foo\.html$ foo.night.html
此例使URLfoo.html在07:00-19:00時指向foo.day.html ,而在其余時間,則指向foo.night.html ,對主頁是一個不錯的功能...
在轉(zhuǎn)變了大批.html文件為.phtml ,使文檔.YYYY過渡成為文檔.XXXX后,如何保持URL的向前兼容(仍然虛擬地存在)?
只須按基準(zhǔn)文件名重寫,并測試帶有新的擴(kuò)展名的文件是否存在,如果存在,則用新的,否則,仍然用原來的。
# backward compatibility ruleset for # rewriting document.html to document.phtml # when and only when document.phtml exists # but no longer document.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 RewriteRule ^(.*)$ $1.phtml [S=1] # else reverse the previous basename cutout RewriteCond %{ENV:WasHTML} ^yes$ RewriteRule ^(.*)$ $1.html
假定已經(jīng)把文件bar.html
改名為foo.html
,需要對老的URL向前兼容,即讓用戶仍然可以使用老的URL,而感覺不到文件被改名了。
通過以下規(guī)則內(nèi)部地重寫老的URL為新的:
RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo\.html$ bar.html
仍然假定已經(jīng)把文件bar.html
改名為foo.html
,需要對老的URL向前兼容,但是要讓用戶得到文件被改名的暗示,即瀏覽器的地址欄中顯示的是新的URL。
作一個HTTP的強(qiáng)制重定向以改變?yōu)g覽器和用戶界面上的顯示:
RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo\.html$ bar.html [R]
至少對重要的頂級頁面,有時候有必要提供依賴于瀏覽器的最佳的內(nèi)容,即對最新的Netscape提供最大化的版本,對Lynx提供最小化的版本,而對其他的瀏覽器則提供一個功能一般的版本。
對此,內(nèi)容協(xié)商無能為力,因?yàn)闉g覽器不提供那種形式的類型,所以只能在HTTP頭"User-Agent"上想辦法。以下規(guī)則集可以完成這個操作:如果HTTP頭"User-Agent"以"Mozilla/3"開頭,則頁面foo.html
被重寫為foo.NS.html
,而后重寫操作終止;如果是"Lynx"或者版本號為1和2的"Mozilla",則重寫為foo.20.html
;而其他所有的瀏覽器收到的頁面則是foo.32.html
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]
假定,需要在我們的名稱空間里加入其他遠(yuǎn)程主機(jī)的頁面。對FTP服務(wù)器,可以用mirror
程序以在本地機(jī)器上維持一個對遠(yuǎn)程數(shù)據(jù)的最新的拷貝;對web服務(wù)器,可以用類似的用于HTTP的webcopy
程序。但這兩種技術(shù)都有一個主要的缺點(diǎn):此本地拷貝必須通過這個程序的執(zhí)行來更新。所以,比較好的方法是,不采用靜態(tài)鏡像,而采用動態(tài)鏡像,即在有數(shù)據(jù)請求時自動更新(遠(yuǎn)程主機(jī)上更新的數(shù)據(jù))。
為此,使用代理吞吐(Proxy Throughput)功能(flag [P]
),以映射遠(yuǎn)程頁面甚至整個遠(yuǎn)程網(wǎng)絡(luò)區(qū)域到我們的名稱空間:
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
這是一種在受防火墻保護(hù)的(內(nèi)部)Intranet(www2.quux-corp.dom
)上保存和維護(hù)實(shí)際數(shù)據(jù),而虛擬地運(yùn)行企業(yè)級(外部)Internet web服務(wù)器(www.quux-corp.dom
)的巧妙的方法。這種方法是外部服務(wù)器在空閑時間從內(nèi)部服務(wù)器取得被請求的數(shù)據(jù)。
首先,必須確保防火墻對內(nèi)部服務(wù)器的保護(hù),并只允許此外部服務(wù)器取得數(shù)據(jù)。對包過濾(packet-filtering)防火墻,可以如下制定防火墻規(guī)則:
ALLOW Host www.quux-corp.dom Port >1024 --> Host www2.quux-corp.dom Port 80 DENY Host * Port * --> Host www2.quux-corp.dom Port 80
按你的實(shí)際配置,只要對上例稍作調(diào)整即可。接著,建立通過代理后臺獲取丟失數(shù)據(jù)的mod_rewrite
規(guī)則:
RewriteRule ^/~([^/]+)/?(.*) /home/$1/.www/$2 RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^/home/([^/]+)/.www/?(.*) http://www2.quux-corp.dom/~$1/pub/$2 [P]
如何均衡www.foo.com
的負(fù)載到www[0-5].foo.com
(一共是6個服務(wù)器)?
這個問題有許多可能的解決方案,在此,我們討論通稱為“基于DNS”的方案,和特殊的使用mod_rewrite
的方案:
最簡單的方法是用BIND
的DNS循環(huán)特性,只要按慣例設(shè)置www[0-9].foo.com
的DNS的A(地址)記錄,如:
www0 IN A 1.2.3.1 www1 IN A 1.2.3.2 www2 IN A 1.2.3.3 www3 IN A 1.2.3.4 www4 IN A 1.2.3.5 www5 IN A 1.2.3.6
然后,增加以下各項(xiàng):
www IN CNAME www0.foo.com. IN CNAME www1.foo.com. IN CNAME www2.foo.com. IN CNAME www3.foo.com. IN CNAME www4.foo.com. IN CNAME www5.foo.com. IN CNAME www6.foo.com.
注意,上述看起來似乎是錯誤的,但事實(shí)上,它的確是BIND
中的一個預(yù)期的特性,而且也可以這樣用。無論如何,現(xiàn)在www.foo.com
已經(jīng)被解析,BIND
可以給出www0-www6
,雖然每次在次序上會有輕微的置換/循環(huán),客戶端的請求可以被分散到各個服務(wù)器。但這并不是一個優(yōu)秀的負(fù)載均衡方案,因?yàn)镈NS解析信息可以被網(wǎng)絡(luò)中其他名稱服務(wù)器緩沖,而一旦www.foo.com
被解析為wwwN.foo.com
,則其后繼請求都將被送往www.foo.com
。但是最終結(jié)果是正確的,因?yàn)檎埱蟮目偭康拇_被分散到各個服務(wù)器了
一種成熟的基于DNS的負(fù)載均衡方法是使用http://www.stanford.edu/~schemers/docs/lbnamed/lbnamed.html的lbnamed
程序,它是一個Perl5程序,帶有若干輔助工具,實(shí)現(xiàn)了真正的基于DNS的負(fù)載均衡。
這是一個使用mod_rewrite
及其代理吞吐特性的方法。首先,在DNS記錄中將www0.foo.com
固定為www.foo.com
,如下:
www IN CNAME www0.foo.com.
其次,將www0.foo.com
轉(zhuǎn)換為一個專職代理服務(wù)器,即由這個機(jī)器把所有到來的URL通過內(nèi)部代理分散到另外5個服務(wù)器(www1-www5
)。為此,必須建立一個規(guī)則集,對所有URL調(diào)用一個負(fù)載均衡腳本lb.pl
。
RewriteEngine on RewriteMap lb prg:/path/to/lb.pl RewriteRule ^/(.+)$ ${lb:$1} [P,L]
以下是lb.pl
:
#!/path/to/perl ## ## lb.pl -- load balancing script ## $| = 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##
www0.foo.com
似乎也會超載呀?答案是:沒錯,它的確會超載,但是它超載的僅僅是簡單的代理吞吐請求!所有諸如SSI、CGI、ePerl等等的處理完全是由其他機(jī)器完成的,這個才是要點(diǎn)。還有一個硬件解決方案。Cisco有一個叫LocalDirector的東西,實(shí)現(xiàn)了TCP/IP層的負(fù)載均衡,事實(shí)上,它是一個位于網(wǎng)站集群前端的電路級網(wǎng)關(guān)。如果你有足夠資金而且的確需要高性能的解決方案,那么可以用這個。
## ## apache-rproxy.conf -- Apache configuration for Reverse Proxy Usage ## # server type ServerType standalone Listen 8000 MinSpareServers 16 StartServers 16 MaxSpareServers 16 MaxClients 16 MaxRequestsPerChild 100 # server operation parameters KeepAlive on MaxKeepAliveRequests 100 KeepAliveTimeout 15 Timeout 400 IdentityCheck off HostnameLookups off # paths to runtime files PidFile /path/to/apache-rproxy.pid LockFile /path/to/apache-rproxy.lock ErrorLog /path/to/apache-rproxy.elog CustomLog /path/to/apache-rproxy.dlog "%{%v/%T}t %h -> %{SERVER}e URL: %U" # unused paths ServerRoot /tmp DocumentRoot /tmp CacheRoot /tmp RewriteLog /dev/null TransferLog /dev/null TypesConfig /dev/null AccessConfig /dev/null ResourceConfig /dev/null # speed up and secure processing <Directory /> Options -FollowSymLinks -SymLinksIfOwnerMatch AllowOverride None </Directory> # the status page for monitoring the reverse proxy <Location /apache-rproxy-status> SetHandler server-status </Location> # enable the URL rewriting engine RewriteEngine on RewriteLogLevel 0 # define a rewriting map with value-lists where # mod_rewrite randomly chooses a particular value RewriteMap server rnd:/path/to/apache-rproxy.conf-servers # make sure the status page is handled locally # and make sure no one uses our proxy except ourself RewriteRule ^/apache-rproxy-status.* - [L] RewriteRule ^(http|ftp)://.* - [F] # now choose the possible servers for particular URL types RewriteRule ^/(.*\.(cgi|shtml))$ to://${server:dynamic}/$1 [S=1] RewriteRule ^/(.*)$ to://${server:static}/$1 # and delegate the generated URL by passing it # through the proxy module RewriteRule ^to://([^/]+)/(.*) http://$1/$2 [E=SERVER:$1,P,L] # and make really sure all other stuff is forbidden # when it should survive the above rules... RewriteRule .* - [F] # enable the Proxy module without caching ProxyRequests on NoCache * # setup URL reverse mapping for redirect reponses ProxyPassReverse / http://www1.foo.dom/ ProxyPassReverse / http://www2.foo.dom/ ProxyPassReverse / http://www3.foo.dom/ ProxyPassReverse / http://www4.foo.dom/ ProxyPassReverse / http://www5.foo.dom/ ProxyPassReverse / http://www6.foo.dom/
## ## apache-rproxy.conf-servers -- Apache/mod_rewrite selection table ## # list of backend servers which serve static # pages (HTML files and Images, etc.) static www1.foo.dom|www2.foo.dom|www3.foo.dom|www4.foo.dom # list of backend servers which serve dynamically # generated page (CGI programs or mod_perl scripts) dynamic www5.foo.dom|www6.foo.dom
網(wǎng)上有許多很巧妙的CGI程序,但是用法晦澀,許多網(wǎng)管棄之不用。即使是Apache的MEME類型的動作處理器,也僅僅在CGI程序不需要在其輸入中包含特殊URL(PATH_INFO
和QUERY_STRINGS
)時才很好用。首先,配置一種新的后綴為.scgi
(安全CGI)文件類型,其處理器是很常見的cgiwrap
程序。問題是:如果使用同類URL規(guī)劃(見上述),而用戶宿主目錄中的一個文件的URL是/u/user/foo/bar.scgi
,可是cgiwrap
要求的URL的格式是/~user/foo/bar.scgi/
,以下規(guī)則解決了這個問題:
RewriteRule ^/[uge]/([^/]+)/\.www/(.+)\.scgi(.*) ... ... /internal/cgi/user/cgiwrap/~$1/$2.scgi$3 [NS,T=application/x-http-cgi]
另外,假設(shè)需要使用其他程序:wwwlog
(顯示access.log
中的一個URL子樹)和wwwidx
(對一個URL子樹運(yùn)行Glimpse),則必須對這些程序提供URL區(qū)域作為其操作對象。比如,對/u/user/foo/
執(zhí)行swwidx
程序的超鏈?zhǔn)沁@樣的:
/internal/cgi/user/swwidx?i=/u/user/foo/
其缺點(diǎn)是,必須同時硬編碼超鏈中的區(qū)域和CGI的路徑,如果重組了這個區(qū)域,就需要花費(fèi)大量時間來修改各個超鏈。
方案是用一個特殊的新的URL格式,自動拼裝CGI參數(shù):
RewriteRule ^/([uge])/([^/]+)(/?.*)/\* /internal/cgi/user/wwwidx?i=/$1/$2$3/ RewriteRule ^/([uge])/([^/]+)(/?.*):log /internal/cgi/user/wwwlog?f=/$1/$2$3
現(xiàn)在,這個搜索到/u/user/foo/
的超鏈簡化成了:
HREF="*"
它會被內(nèi)部地自動轉(zhuǎn)換為
/internal/cgi/user/wwwidx?i=/u/user/foo/
如此,可以為使用":log
"的超鏈,拼裝出調(diào)用CGI程序的參數(shù)。
如何無縫轉(zhuǎn)換靜態(tài)頁面foo.html
為動態(tài)的foo.cgi
,而不為瀏覽器/用戶所察覺。
只須重寫此URL為CGI-script ,以強(qiáng)制為可以作為CGI-script運(yùn)行的正確的MIME類型。如此,對/~quux/foo.html
的請求其實(shí)會執(zhí)行/~quux/foo.cgi
。
RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo\.html$ foo.cgi [T=application/x-httpd-cgi]
這是一個很難解的功能:動態(tài)生成的靜態(tài)頁面,即它應(yīng)該作為靜態(tài)頁面發(fā)送(從文件系統(tǒng)中讀出,然后直接發(fā)出去),但是如果它丟失了,則由服務(wù)器動態(tài)生成。這樣,可以靜態(tài)地提供CGI生成的頁面,除非有人(或者是一個cronjob)刪除了這些靜態(tài)頁面,而且其內(nèi)容可以得到更新。
RewriteCond %{REQUEST_FILENAME} !-s RewriteRule ^page\.html$ page.cgi [T=application/x-httpd-cgi,L]
這樣,如果page.html
不存在或者文件大小為null ,則對page.html
的請求會導(dǎo)致page.cgi
的運(yùn)行。其中奧妙在于page.cgi
是一個將輸出寫入page.html
的(同時也寫入STDOUT
)的常規(guī)的CGI腳本,執(zhí)行完畢,服務(wù)器則將page.html
的內(nèi)容發(fā)出。如果網(wǎng)管需要強(qiáng)制更新其內(nèi)容,只須刪除page.html
即可(通常由一個cronjob完成)。
建立一個復(fù)雜的頁面,能夠在用編輯器寫了一個更新的版本時自動在瀏覽器上得到刷新,這不是很好嗎?這可能嗎?
這是可行的! 這需要綜合利用MIME多成分、web服務(wù)器的NPH和mod_rewrite
的URL操控特性。首先,建立一個新的URL特性:對在文件系統(tǒng)中更新時需要刷新的所有URL加上":refresh
" 。
RewriteRule ^(/[uge]/[^/]+/?.*):refresh /internal/cgi/apache/nph-refresh?f=$1
然后,修改URL
/u/foo/bar/page.html:refresh
以內(nèi)部地操控此URL
/internal/cgi/apache/nph-refresh?f=/u/foo/bar/page.html
接著就是NPH-CGI腳本部分了。雖然,人們常說"將此作為一個練習(xí)留給讀者",但我還是給出答案了。
#!/sw/bin/perl ## ## nph-refresh -- NPH/CGI script for auto refreshing pages ## Copyright (c) 1997 Ralf S. Engelschall, All Rights Reserved. ## $| = 1; # split the QUERY_STRING variable @pairs = split(/&/, $ENV{'QUERY_STRING'}); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $name =~ tr/A-Z/a-z/; $name = 'QS_' . $name; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; eval "\$$name = \"$value\""; } $QS_s = 1 if ($QS_s eq ''); $QS_n = 3600 if ($QS_n eq ''); if ($QS_f eq '') { print "HTTP/1.0 200 OK\n"; print "Content-type: text/html\n\n"; print "<b>ERROR</b>: No file given\n"; exit(0); } if (! -f $QS_f) { print "HTTP/1.0 200 OK\n"; print "Content-type: text/html\n\n"; print "<b>ERROR</b>: File $QS_f not found\n"; exit(0); } sub print_http_headers_multipart_begin { print "HTTP/1.0 200 OK\n"; $bound = "ThisRandomString12345"; print "Content-type: multipart/x-mixed-replace;boundary=$bound\n"; &print_http_headers_multipart_next; } sub print_http_headers_multipart_next { print "\n--$bound\n"; } sub print_http_headers_multipart_end { print "\n--$bound--\n"; } sub displayhtml { local($buffer) = @_; $len = length($buffer); print "Content-type: text/html\n"; print "Content-length: $len\n\n"; print $buffer; } sub readfile { local($file) = @_; local(*FP, $size, $buffer, $bytes); ($x, $x, $x, $x, $x, $x, $x, $size) = stat($file); $size = sprintf("%d", $size); open(FP, "<$file"); $bytes = sysread(FP, $buffer, $size); close(FP); return $buffer; } $buffer = &readfile($QS_f); &print_http_headers_multipart_begin; &displayhtml($buffer); sub mystat { local($file) = $_[0]; local($time); ($x, $x, $x, $x, $x, $x, $x, $x, $x, $mtime) = stat($file); return $mtime; } $mtimeL = &mystat($QS_f); $mtime = $mtime; for ($n = 0; $n < $QS_n; $n++) { while (1) { $mtime = &mystat($QS_f); if ($mtime ne $mtimeL) { $mtimeL = $mtime; sleep(2); $buffer = &readfile($QS_f); &print_http_headers_multipart_next; &displayhtml($buffer); sleep(5); $mtimeL = &mystat($QS_f); last; } sleep($QS_s); } } &print_http_headers_multipart_end; exit(0); ##EOF##
Apache的<VirtualHost>
功能很強(qiáng),在有幾十個虛擬主機(jī)的情況下運(yùn)行得很好,但是如果你是ISP,需要提供幾百個虛擬主機(jī),那么這就不是一個最佳的選擇了。
為此,需要用代理吞吐(Proxy Throughput)功能(flag [P]
)映射遠(yuǎn)程頁面甚至整個遠(yuǎn)程網(wǎng)絡(luò)區(qū)域到自己的名稱空間:
## ## vhost.map ## www.vhost1.dom:80 /path/to/docroot/vhost1 www.vhost2.dom:80 /path/to/docroot/vhost2 : www.vhostN.dom:80 /path/to/docroot/vhostN
## ## httpd.conf ## : # use the canonical hostname on redirects, etc. UseCanonicalName on : # add the virtual host in front of the CLF-format CustomLog /path/to/access_log "%{VHOST}e %h %l %u %t \"%r\" %>s %b" : # enable the rewriting engine in the main server RewriteEngine on # define two maps: one for fixing the URL and one which defines # the available virtual hosts with their corresponding # DocumentRoot. RewriteMap lowercase int:tolower RewriteMap vhost txt:/path/to/vhost.map # Now do the actual virtual host mapping # via a huge and complicated single rule: # # 1. make sure we don't map for common locations RewriteCond %{REQUEST_URL} !^/commonurl1/.* RewriteCond %{REQUEST_URL} !^/commonurl2/.* : RewriteCond %{REQUEST_URL} !^/commonurlN/.* # # 2. make sure we have a Host header, because # currently our approach only supports # virtual hosting through this header RewriteCond %{HTTP_HOST} !^$ # # 3. lowercase the hostname RewriteCond ${lowercase:%{HTTP_HOST}|NONE} ^(.+)$ # # 4. lookup this hostname in vhost.map and # remember it only when it is a path # (and not "NONE" from above) RewriteCond ${vhost:%1} ^(/.*)$ # # 5. finally we can map the URL to its docroot location # and remember the virtual host for logging puposes RewriteRule ^/(.*)$ %1/$1 [E=VHOST:${lowercase:%{HTTP_HOST}}] :
如何阻止一個完全匿名的robot取得特定網(wǎng)絡(luò)區(qū)域的頁面?一個/robots.txt
文件可以包含若干"robot排除協(xié)議"的行,但不足以阻止此類robot。
可以用一個規(guī)則集以拒絕對網(wǎng)絡(luò)區(qū)域/~quux/foo/arc/
(對一個很深的目錄區(qū)域進(jìn)行列表可能會使服務(wù)器產(chǎn)生很大的負(fù)載)的訪問。還必須確保僅阻止特定的robot,就是說,僅僅阻止robot訪問主機(jī)是不夠的,這樣會同時也阻止了用戶訪問該主機(jī)。為此,就需要對HTTP頭的User-Agent信息作匹配。
RewriteCond %{HTTP_USER_AGENT} ^NameOfBadRobot.* RewriteCond %{REMOTE_ADDR} ^123\.45\.67\.[8-9]$ RewriteRule ^/~quux/foo/arc/.+ - [F]
假設(shè),http://www.quux-corp.de/~quux/
有一些內(nèi)嵌圖片的頁面,這些圖片很好,所以就有人用超鏈連到他們自己的頁面中了。由于這樣徒然增加了我們的服務(wù)器的流量,因此,我們不愿意這種事情發(fā)生。
雖然,我們不能100%地保護(hù)這些圖片不被寫入別人的頁面,但至少可以對發(fā)出HTTP Referer頭的瀏覽器加以限制。
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]
如何拒絕一批外部列表中的主機(jī)對我們服務(wù)器的使用?
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]
如何拒絕某個主機(jī)或者來自特定主機(jī)的用戶使用Apache代理?
首先,要確保Apache web服務(wù)器在編譯時配置文件中mod_rewrite
在mod_proxy
的下面!使它在mod_proxy
之前被調(diào)用。然后,如下拒絕某個主機(jī)...
RewriteCond %{REMOTE_HOST} ^badhost\.mydomain\.com$ RewriteRule !^http://[^/.]\.mydomain.com.* - [F]
...如下拒絕user@host-dependent:
RewriteCond %{REMOTE_IDENT}@%{REMOTE_HOST} ^badguy@badhost\.mydomain\.com$ RewriteRule !^http://[^/.]\.mydomain.com.* - [F]
有時候,會需要一種非常特殊的認(rèn)證,即對一組明確指定的用戶,允許其訪問,而沒有(在使用mod_access
的基本認(rèn)證方法時可能會出現(xiàn)的)任何提示。
可是使用一個重寫條件列表來排除所有的朋友:
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]
如何配置一個基于HTTP頭"Referer"的反射器以反射到任意數(shù)量的提交頁面?
使用這個很巧妙的規(guī)則集...
RewriteMap deflector txt:/path/to/deflector.map RewriteCond %{HTTP_REFERER} !="" RewriteCond ${deflector:%{HTTP_REFERER}} ^-$ RewriteRule ^.* %{HTTP_REFERER} [R,L] RewriteCond %{HTTP_REFERER} !="" RewriteCond ${deflector:%{HTTP_REFERER}|NOT-FOUND} !=NOT-FOUND RewriteRule ^.* ${deflector:%{HTTP_REFERER}} [R,L]
... 并結(jié)合對應(yīng)的重寫映射地圖:
## ## deflector.map ## http://www.badguys.com/bad/index.html - http://www.badguys.com/bad/index2.html - http://www.badguys.com/bad/index3.html http://somewhere.com/
它可以自動將請求(在映射地圖中指定了"-
"值的時候)反射回其提交頁面,或者(在映射地圖中URL有第二個參數(shù)時)反射到一個特定的URL。
一個常見的問題是如何解決似乎無法用mod_rewrite
解決的FOO/BAR/QUUX/之類的問題?
可以使用一個與RewriteMap
功能相同的外部RewriteMap
程序,一旦它在Apache啟動時被執(zhí)行,則從STDIN
接收被請求的URL ,并將處理過(通常是重寫過的)的URL(以相同順序)在STDOUT
輸出。
RewriteEngine on RewriteMap quux-map prg:/path/to/map.quux.pl RewriteRule ^/~quux/(.*)$ /~quux/${quux-map:$1}
#!/path/to/perl # disable buffered I/O which would lead # to deadloops for the Apache server $| = 1; # read URLs one per line from stdin and # generate substitution URL on stdout while (<>) { s|^foo/|bar/|; print $_; }
這是一個作演示的例子,只是把所有的URL /~quux/foo/...
重寫為 /~quux/bar/...
,而事實(shí)上,可以把它修改以獲得任何你需要的功能。但是要注意,雖然一般用戶都可以使用,可是只有系統(tǒng)管理員才可以定義這樣的地圖。