Apache的mod_rewrite是提供了強大URL操作的殺手級的模塊,可以實(shí)現幾乎所有你夢(mèng)想的URL操作類(lèi)型,其代價(jià)是你必須接受其復雜性,因為mod_rewrite的主要障礙就是初學(xué)者不容易理解和運用,即使是Apache專(zhuān)家有時(shí)也會(huì )發(fā)掘出mod_rewrite的新用途。
換句話(huà)說(shuō):對mod_rewrite,或者是打退堂鼓永不再用,或者是喜歡它并一生受用。本文試圖通過(guò)對已有方案的表述來(lái)創(chuàng )造一個(gè)成功的開(kāi)端,以免你放棄。
我自己創(chuàng )造和收集了許多實(shí)踐方案,不要有畏懼心理,從這些例子開(kāi)始學(xué)習URL重寫(xiě)的黑匣子吧。
mod_alias和mod_userdir時(shí)要增加[PT]標志,或者重寫(xiě).htaccess而不是單個(gè)服務(wù)器中的規則集。對一個(gè)特定的規則集應該首先去理解而后再去用以避免出問(wèn)題。在有些網(wǎng)站服務(wù)器上,一個(gè)資源會(huì )擁有多個(gè)URL,在實(shí)際應用和發(fā)布中應該被使用的是規范的URL,其他的則是簡(jiǎn)寫(xiě)或者是內部使用的。無(wú)論用戶(hù)在請求中使用什么形式的URL,他最終看見(jiàn)的都應該是規范的URL。
對所有的不規范的URL執行一個(gè)外部的HTTP重定向,以改變它在瀏覽器地址欄中的顯示及其后繼的請求。下例中的規則集用規范的/u/user替換/~user,并修正了/u/user所遺漏的后綴的斜杠。
RewriteRule ^/~([^/]+)/?(.*) /u/$1/$2 [R] RewriteRule ^/([uge])/([^/]+)$ /$1/$2/ [R]
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]
RewriteCond %{HTTP_HOST} !^fully\.qualified\.domain\.name [NC]
RewriteCond %{HTTP_HOST} !^$
RewriteRule ^/(.*) http://fully.qualified.domain.name/$1 [L,R]
DocumentRoot通常,網(wǎng)站服務(wù)器的/e/www/(WWW的主頁(yè))、/e/sww/ (Intranet的主頁(yè))等等,而DocumentRoot指向了/e/www/,則必須保證此數據池中的所有內嵌的圖片和其他元素對后繼請求有效。
只須重定向URL /到/e/www/即可。這個(gè)方案看起來(lái)很簡(jiǎn)單,但只是有了mod_rewrite模塊的支持,它才簡(jiǎn)單,因為傳統的URL Aliases機制(由mod_alias及其相關(guān)模塊提供)只是作了一個(gè)前綴匹配,DocumentRoot是一個(gè)對所有URL的前綴,因而無(wú)法實(shí)現這樣的重定向。而用mod_rewrite的確很簡(jiǎn)單:
RewriteEngine on RewriteRule ^/$ /e/www/ [R]
每個(gè)網(wǎng)管對引用目錄后綴斜杠的問(wèn)題都有一本苦經(jīng),如果遺漏了,服務(wù)器會(huì )產(chǎn)生一個(gè)錯誤,因為如果請求是/~quux/foo而不是/~quux/foo/,服務(wù)器會(huì )去找一個(gè)叫foo的文件,而它是一個(gè)目錄,所以就報錯了。事實(shí)上,大多數情況下,它自己會(huì )試圖修正這個(gè)錯誤,但是有時(shí)候需要你手工糾正,比如,在重寫(xiě)了許多CGI腳本中的復雜的URL以后。
解決這個(gè)微妙問(wèn)題的方案是讓服務(wù)器自動(dòng)添加后綴的斜杠。對此,必須使用一個(gè)外部的重定向,使瀏覽器正確地處理后繼的對諸如圖片的請求。如果僅僅作一個(gè)內部的重寫(xiě),可能只對目錄頁(yè)面有效,而對內嵌有使用相對URL的圖片的頁(yè)面則無(wú)效,因為瀏覽器有請求內嵌目標的可能。比如,如果不用外部重定向,/~quux/foo/index.html頁(yè)面中對image.gif的請求,其結果將是/~quux/image.gif!。
所以,應該這樣寫(xiě):
RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo$ foo/ [R]
又懶又瘋狂的做法是把這些寫(xiě)入其宿主目錄中的頂級.htaccess中,但是須注意,如此會(huì )帶來(lái)一些處理上的開(kāi)銷(xiāo)。
RewriteEngine on
RewriteBase /~quux/
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^(.+[^/])$ $1/ [R]
我們希望在一個(gè)Intranet集群網(wǎng)站中,對所有WWW服務(wù)器建立一個(gè)同類(lèi)的一致性的URL規劃,也就是,所有的URL(對單個(gè)服務(wù)器來(lái)說(shuō),是本地的依賴(lài)于此服務(wù)器的!)是獨立于服務(wù)器的!我們需要的是一個(gè)具有獨立于服務(wù)器的一致性規劃的WWW名稱(chēng)空間,即,URL不需要包含正確的物理的目標服務(wù)器,而由集群本身來(lái)自動(dòng)定位物理的目標主機。
首先,目標服務(wù)器的信息來(lái)自(產(chǎn)生)于包含有用戶(hù)、組以及實(shí)體的外部地圖,其格式形如:
user1 server_of_user1 user2 server_of_user2 : :
這些信息被存入map.xxx-to-host文件。其次,如果URL在一個(gè)服務(wù)器上無(wú)效,需要引導所有的服務(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
以下規則集依靠地圖文件來(lái)完成這個(gè)操作(假定,如果一個(gè)用戶(hù)在地圖中沒(méi)有對應的項,則使用server0為默認服務(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)管在建立一個(gè)新的網(wǎng)站服務(wù)器時(shí),都會(huì )有這樣的要求:重定向一個(gè)網(wǎng)站服務(wù)器上的所有宿主目錄到另一個(gè)網(wǎng)站服務(wù)器。
很簡(jiǎn)單,用mod_rewrite。在老的網(wǎng)站服務(wù)器上重定向所有的URL /~user/anypath到http://newserver/~user/anypath。
RewriteEngine on RewriteRule ^/~(.+) http://newserver/~$1 [R,L]
一些擁有幾千個(gè)用戶(hù)的網(wǎng)站通常都使用結構化的宿主目錄規劃,即,每個(gè)宿主目錄位于一個(gè)帶有特定前綴比如其用戶(hù)名的第一個(gè)字符的子目錄下。那么,/~foo/anypath代表/home/f/foo/.www/anypath,而/~bar/anypath代表/home/b/bar/.www/anypath。
可以使用下列規則集來(lái)擴展~以達到上述目的。
RewriteEngine on RewriteRule ^/~(([a-z])[a-z0-9]+)(.*) /home/$2/$1/.www$3
這是一個(gè)不加雕琢的例子:一個(gè)大量使用針對目錄的規則集以實(shí)現平滑觀(guān)感,而從來(lái)不用調整數據結構的殺手級的應用。背景:net.sw從1992年開(kāi)始,存放了我收集的免費的有效的Unix軟件包。它是我的愛(ài)好也是我的工作,因為在學(xué)習計算機科學(xué)的同時(shí),業(yè)余時(shí)間還做了多年的系統和網(wǎng)絡(luò )的管理員。每周我都需要整理軟件,因而建立了一個(gè)層次很深的目錄結構來(lái)存放各種軟件包:
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月,我決定通過(guò)一個(gè)漂亮的Web接口公開(kāi)我的收藏?!捌痢笔侵柑峁┮粋€(gè)接口以直接瀏覽整個(gè)目錄結構,同時(shí)不對這個(gè)結構做任何改變 - 甚至也不在結構頂部放置CGI腳本。為什么呢?因為這個(gè)結構還要能夠被FTP訪(fǎng)問(wèn),而且我不希望其中有任何Web或者CGI的成分。
這個(gè)方案分為兩個(gè)部分:第一個(gè)部分,是用于在空閑時(shí)間建立所有目錄頁(yè)面的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/子目錄包含了上述目錄結構,即實(shí)在的net.sw,由rdist在需要的時(shí)候自動(dòng)更新。第二個(gè)部分的遺留問(wèn)題是:如何連接這兩個(gè)結構為一個(gè)平滑觀(guān)感的URL樹(shù)?我希望在運行適當的CGI腳本而使用各種URL的時(shí)候,使用戶(hù)感覺(jué)不到DATA/目錄的存在。方案如下:首先,我把下列配置放在服務(wù)器上DocumentRoot中的針對目錄的配置文件里,以重寫(xiě)公布的URL /net.sw/ 為內部路徑 /e/netsw:
RewriteRule ^net.sw$ net.sw/ [R] RewriteRule ^net.sw/(.*)$ e/netsw/$1
第一條規則是針對遺漏后綴斜杠的請求的!第二條規則才是真正實(shí)現功能的。接著(zhù),就是放在針對目錄的配置文件/e/netsw/.www/.wwwacl中的殺手級的配置了:
Options ExecCGI FollowSymLinks Includes MultiViews RewriteEngine on # we are reached via /net.sw/ prefix RewriteBase /net.sw/ # first we rewrite the root dir to # the handling cgi script RewriteRule ^$ netsw-home.cgi [L] RewriteRule ^index\.html$ netsw-home.cgi [L] # strip out the subdirs when # the browser requests us from perdir pages RewriteRule ^.+/(netsw-[^/]+/.+)$ $1 [L] # and now break the rewriting for local files 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] # anything else is a subdir which gets handled # by another cgi script RewriteRule !^netsw-lsdir\.cgi.* - [C] RewriteRule (.*) netsw-lsdir.cgi/$1
閱讀提示:
L(最后),和無(wú)對應項(‘-‘)
!(非),和標志C (鏈)
mod_imap許多人都希望在從NCSA網(wǎng)站服務(wù)器向較現代的Apache網(wǎng)站服務(wù)器轉移中實(shí)現平滑過(guò)渡,即希望老的NCSA imagemap程序能在A(yíng)pache的較現代的mod_imap支持下正常運作。但問(wèn)題在于,到處都是通過(guò)/cgi-bin/imagemap/path/to/page.map引用imagemap程序的連接,而在A(yíng)pache下,應該寫(xiě)成/path/to/page.map。
使用全局規則在空閑時(shí)間去除所有這些請求的前綴:
RewriteEngine on RewriteRule ^/cgi-bin/imagemap(.*) $1 [PT]
有時(shí)會(huì )有必要使網(wǎng)站服務(wù)器在多個(gè)目錄中搜索頁(yè)面,對此,MultiViews或者其他技術(shù)無(wú)能為力。
編制一個(gè)明確的規則集以搜索目錄中的文件。
RewriteEngine on
# first try to find it in custom/...
# ...and if found stop and be happy:
RewriteCond /your/docroot/dir1/%{REQUEST_FILENAME} -f
RewriteRule ^(.+) /your/docroot/dir1/$1 [L]
# second try to find it in pub/...
# ...and if found stop and be happy:
RewriteCond /your/docroot/dir2/%{REQUEST_FILENAME} -f
RewriteRule ^(.+) /your/docroot/dir2/$1 [L]
# else go on for other Alias or ScriptAlias directives,
# etc.
RewriteRule ^(.+) - [PT]
如果希望保持請求之間的狀態(tài)信息,但又不希望使用CGI來(lái)包裝所有頁(yè)面,而只通過(guò)分離URL中的有用信息來(lái)編碼。
可以用一個(gè)規則集來(lái)分離出狀態(tài)信息,并設置環(huán)境變量以備此后用于XSSI或CGI。如此,一個(gè)/foo/S=java/bar/的URL會(huì )被解析為/foo/bar/,而環(huán)境變量STATUS則被設置為"java"。
RewriteEngine on RewriteRule ^(.*)/S=([^/]+)/(.*) $1/$3 [E=STATUS:$2]
如果需要為用戶(hù)username支持一個(gè)www.username.host.domain.com的主頁(yè),但不是用在此機器上建虛擬主機的方法,而是用僅在此機器上增加一個(gè)DNS記錄的方法實(shí)現。
對HTTP/1.0的請求,這是無(wú)法實(shí)現的;但是對HTTP/1.1的在HTTP頭中包含有主機名的請求,可以用以下規則集來(lái)內部地重寫(xiě)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
對不是來(lái)自本地域ourdomain.com的外來(lái)訪(fǎng)問(wèn)者的請求,重定向其宿主目錄URL到另一個(gè)網(wǎng)站服務(wù)器www.somewhere.com,有時(shí)這種做法也會(huì )用在虛擬主機的上下文中。
只須一個(gè)重寫(xiě)條件:
RewriteEngine on
RewriteCond %{REMOTE_HOST} !^.+\.ourdomain\.com$
RewriteRule ^(/~.+) http://www.somewhere.com/$1 [R,L]
如何重寫(xiě)URL以重定向對網(wǎng)站服務(wù)器A的失敗請求到服務(wù)器B,是一個(gè)常見(jiàn)的問(wèn)題。一般,可以用Perl寫(xiě)的CGI腳本通過(guò)ErrorDocument來(lái)解決,此外,還有mod_rewrite方案。但是須注意,這種方法的執行效率不如用ErrorDocument的CGI腳本!
第一種方案,有最好的性能而靈活性欠佳,出錯概率小所以安全:
RewriteEngine on
RewriteCond /your/docroot/%{REQUEST_FILENAME} !-f
RewriteRule ^(.+) http://webserverB.dom/$1
但是其問(wèn)題在于,它只對位于DocumentRoot中的頁(yè)面有效。雖然可以增加更多的條件(比如同時(shí)還處理宿主目錄,等等),但是還有一個(gè)更好的方法:
RewriteEngine on
RewriteCond %{REQUEST_URI} !-U
RewriteRule ^(.+) http://webserverB.dom/$1
這種方法使用了mod_rewrite提供的“向前參照(look-ahead)”的功能,是一種對所有URL類(lèi)型都有效而且安全的方法。但是,對網(wǎng)站服務(wù)器的性能會(huì )有影響,所以如果網(wǎng)站服務(wù)器有一個(gè)強大的CPU,那就用這個(gè)方法。而在慢速機器上,可以用第一種方法,或者用性能更好的ErrorDocument CGI腳本。
有時(shí)候,我們會(huì )需要更多的對重定向URL的(有關(guān)字符轉義機制方面的)控制。通常,Apache內核中的URL轉義函數uri_escape()同時(shí)還會(huì )對anchor轉義,即,類(lèi)似"url#anchor"的URL,因此,你不能用mod_rewrite對此類(lèi)URL直接重定向。那么如何實(shí)現呢?
必須用NPH-CGI腳本使它自己重定向,因為對NPH(non-parseable headers [無(wú)須解析的HTTP頭])不會(huì )發(fā)生轉義操作。首先,在針對服務(wù)器的配置中(應該位于所有重寫(xiě)規則的最后),引入一種新的URL類(lèi)型xredirect::
RewriteRule ^xredirect:(.+) /path/to/nph-xredirect.cgi/$1 [T=application/x-httpd-cgi,L]
以強制所有帶xredirect:前綴的URL被傳送到如下的nph-xredirect.cgi程序:
#!/path/to/perl
##
## nph-xredirect.cgi -- NPH/CGI script for extended redirects
## Copyright (c) 1997 Ralf S. Engelschall, All Rights Reserved.
##
$| = 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類(lèi)型的方法,包括不被mod_rewrite直接支持的類(lèi)型。所以,還可以這樣重定向news:newsgroup:
RewriteRule ^anyurl xredirect:news:newsgroup
[R]或[R,L],因為xredirect:會(huì )在稍后被其特殊的傳送規則擴展。你知道http://www.perl.com/CPAN的CPAN(Comprehensive Perl Archive Network)嗎?它實(shí)現了一個(gè)重定向以提供,全世界的CPAN鏡像中離訪(fǎng)問(wèn)者最近的一個(gè)FTP站點(diǎn),也可以稱(chēng)之為FTP訪(fǎng)問(wèn)多路復用服務(wù)。CPAN是通過(guò)CGI腳本實(shí)現的,那么用mod_rewrite如何實(shí)現呢?
首先,我們注意到RewriteMap實(shí)現。利用鏈式規則集,并用頂級域名作為查找多路復用地圖的鍵,可以這樣做:
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##
在頁(yè)面內容依時(shí)間不同而變化的場(chǎng)合,比如重定向特定頁(yè)面,許多網(wǎng)管仍然采用CGI腳本的方法,如何用mod_rewrite來(lái)實(shí)現呢?
有許多類(lèi)似TIME_xxx的變量可以用在重寫(xiě)條件中,利用<STRING,>STRING和=STRING的類(lèi)型比較,并加以連接,就可以實(shí)現依賴(lài)于時(shí)間的重寫(xiě):
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
此例使URL foo.html在07:00-19:00時(shí)指向foo.day.html,而在其余時(shí)間,則指向foo.night.html,對主頁(yè)是一個(gè)不錯的功能...
在轉變了大批.html文件為.phtml,使文檔.YYYY過(guò)渡成為文檔.XXXX后,如何保持URL的向前兼容(仍然虛擬地存在)?
只須按基準文件名重寫(xiě),并測試帶有新的擴展名的文件是否存在,如果存在,則用新的,否則,仍然用原來(lái)的。
# 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向前兼容,即讓用戶(hù)仍然可以使用老的URL,而感覺(jué)不到文件被改名了。
通過(guò)以下規則內部地重寫(xiě)老的URL為新的:
RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo\.html$ bar.html
仍然假定已經(jīng)把文件bar.html改名為foo.html,需要對老的URL向前兼容,但是要讓用戶(hù)得到文件被改名的暗示,即,其瀏覽器的地址欄中顯示的是新的URL。
作一個(gè)HTTP的強制重定向以改變?yōu)g覽器和用戶(hù)界面上的顯示:
RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo\.html$ bar.html [R]
至少對重要的頂級頁(yè)面,有時(shí)候有必要提供依賴(lài)于瀏覽器的最佳的內容,即對最新的Netscape提供最大化的版本,對Lynx提供最小化的版本,而對其他的瀏覽器則提供一個(gè)功能一般的版本。
對此,內容協(xié)商無(wú)能為力,因為瀏覽器不提供其那種形式的類(lèi)型,所以只能在HTTP頭"User-Agent"上想辦法。以下規則集可以完成這個(gè)操作:如果HTTP頭"User-Agent"以"Mozilla/3"開(kāi)頭,則頁(yè)面foo.html被重寫(xiě)為foo.NS.html,而后重寫(xiě)操作終止;如果是"Lynx"或者版本號為1和2的"Mozilla",則重寫(xiě)為foo.20.html;而其他所有的瀏覽器收到的頁(yè)面則是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]
假定,需要在我們的名稱(chēng)空間里加入其他遠程主機的頁(yè)面。對FTP服務(wù)器,可以用mirror程序以在本地機器上維持一個(gè)對遠程數據的最新的拷貝;對網(wǎng)站服務(wù)器,可以用類(lèi)似的用于HTTP的webcopy程序。但這兩種技術(shù)都有一個(gè)主要的缺點(diǎn):此本地拷貝必須通過(guò)這個(gè)程序的執行來(lái)更新。所以,比較好的方法是,不采用靜態(tài)鏡像,而采用動(dòng)態(tài)鏡像,即,在有數據請求時(shí)自動(dòng)更新(遠程主機上更新的數據)。
為此,使用Proxy Throughput功能(flag [P]),以映射遠程頁(yè)面甚至整個(gè)遠程網(wǎng)絡(luò )區域到我們的名稱(chēng)空間:
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
這是一種在受防火墻保護的(內部的)Intranet(www2.quux-corp.dom)上保存和維護實(shí)際數據,而虛擬地運行企業(yè)級(外部的)Internet網(wǎng)站服務(wù)器(www.quux-corp.dom)的巧妙的方法。這種方法是外部服務(wù)器在空閑時(shí)間從內部服務(wù)器取得被請求的數據。
首先,必須確保防火墻對內部服務(wù)器的保護,并只允許此外部服務(wù)器取得數據。對包過(guò)濾(packet-filtering)防火墻,可以如下制定防火墻規則:
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í)際配置,只要對上例稍作調整即可。接著(zhù),建立通過(guò)代理后臺獲取丟失數據的mod_rewrite規則:
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的負載到www[0-5].foo.com(一共是6個(gè)服務(wù)器)?
這個(gè)問(wèn)題有許多可能的解決方案,在此,我們討論通稱(chēng)為“基于DNS(DNS-based)的”方案,和特殊的使用mod_rewrite的方案:
最簡(jiǎn)單的方法是用BIND的DNS循環(huán)特性,只要按慣例設置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
然后,增加以下各項:
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.
注意,上述看起來(lái)似乎是錯誤的,但事實(shí)上,它的確是BIND中的一個(gè)預期的特性,而且也可以這樣用。無(wú)論如何,現在www.foo.com已經(jīng)被解析,BIND可以給出www0-www6 - 雖然每次在次序上會(huì )有輕微的置換/循環(huán),客戶(hù)端的請求可以被分散到各個(gè)服務(wù)器??墒?,這并不是一個(gè)優(yōu)秀的負載均衡方案,因為,DNS解析信息可以被網(wǎng)絡(luò )中其他名稱(chēng)服務(wù)器緩沖,而一旦www.foo.com被解析為wwwN.foo.com,則其后繼請求都將被送往www.foo.com。但是最終結果是正確的,因為請求的總量的確被分散到各個(gè)服務(wù)器了
一種成熟的基于DNS的負載均衡方法是使用http://www.stanford.edu/~schemers/docs/lbnamed/lbnamed.html的lbnamed程序,它是一個(gè)Perl 5程序,帶有若干輔助工具,實(shí)現了真正的基于DNS的負載均衡。
這是一個(gè)使用mod_rewrite及其代理吞吐特性的方法。首先,在DNS記錄中,將www0.foo.com固定為www.foo.com,如下:
www IN CNAME www0.foo.com.
其次,將www0.foo.com轉換為一個(gè)專(zhuān)職代理服務(wù)器,即,由這個(gè)機器把所有到來(lái)的URL通過(guò)內部代理分散到另外5個(gè)服務(wù)器(www1-www5)。為此,必須建立一個(gè)規則集,對所有URL調用一個(gè)負載均衡腳本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似乎也會(huì )超載呀?答案是:沒(méi)錯,它的確會(huì )超載,但是它超載的僅僅是簡(jiǎn)單的代理吞吐請求!所有諸如SSI、CGI、ePerl等等的處理完全是由其他機器完成的,這個(gè)才是要點(diǎn)。還有一個(gè)硬件解決方案。Cisco有一個(gè)叫LocalDirector的東西,實(shí)現了TCP/IP層的負載均衡,事實(shí)上,它是一個(gè)位于網(wǎng)站集群前端的電路級網(wǎng)關(guān)。如果你有足夠資金而且的確需要高性能的解決方案,那么可以用這個(gè)。
##
## 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類(lèi)型的動(dòng)作處理器,也僅僅在CGI程序不需要在其輸入中包含特殊URL(PATH_INFO和QUERY_STRINGS)時(shí)才很好用。首先,配置一種新的后綴為.scgi(for secure CGI)文件類(lèi)型,其處理器是很常見(jiàn)的cgiwrap程序。問(wèn)題是:如果使用同類(lèi)URL規劃(見(jiàn)上述),而用戶(hù)宿主目錄中的一個(gè)文件的URL是/u/user/foo/bar.scgi,可是cgiwrap要求的URL的格式是/~user/foo/bar.scgi/,以下規則解決了這個(gè)問(wèn)題:
RewriteRule ^/[uge]/([^/]+)/\.www/(.+)\.scgi(.*) ... ... /internal/cgi/user/cgiwrap/~$1/$2.scgi$3 [NS,T=application/x-http-cgi]
另外,假設需要使用其他程序:wwwlog(顯示access.log中的一個(gè)URL子樹(shù))和wwwidx(對一個(gè)URL子樹(shù)運行Glimpse),則必須對這些程序提供URL區域作為其操作對象。比如,對/u/user/foo/執行swwidx程序的超鏈是這樣的:
/internal/cgi/user/swwidx?i=/u/user/foo/
其缺點(diǎn)是,必須同時(shí)硬編碼超鏈中的區域和CGI的路徑,如果重組了這個(gè)區域,就需要花費大量時(shí)間來(lái)修改各個(gè)超鏈。
方案是用一個(gè)特殊的新的URL格式,自動(dòng)拼裝CGI參數:
RewriteRule ^/([uge])/([^/]+)(/?.*)/\* /internal/cgi/user/wwwidx?i=/$1/$2$3/ RewriteRule ^/([uge])/([^/]+)(/?.*):log /internal/cgi/user/wwwlog?f=/$1/$2$3
現在,這個(gè)搜索到/u/user/foo/的超鏈簡(jiǎn)化成了:
HREF="*"
它會(huì )被內部地自動(dòng)轉換為
/internal/cgi/user/wwwidx?i=/u/user/foo/
如此,可以為使用:log的超鏈,拼裝出調用CGI程序的參數。
如何無(wú)縫轉換靜態(tài)頁(yè)面foo.html為動(dòng)態(tài)的foo.cgi,而不為瀏覽器/用戶(hù)所察覺(jué)。
只須重寫(xiě)此URL為CGI-script,以強制為可以作為CGI-script運行的正確的MIME類(lèi)型。如此,對/~quux/foo.html的請求其實(shí)會(huì )執行/~quux/foo.cgi。
RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo\.html$ foo.cgi [T=application/x-httpd-cgi]
這是一個(gè)很難解的功能:動(dòng)態(tài)生成的靜態(tài)頁(yè)面,即,它應該作為靜態(tài)頁(yè)面發(fā)送(從文件系統中讀出,然后直接發(fā)出去),但是如果它丟失了,則由服務(wù)器動(dòng)態(tài)生成。如此,可以靜態(tài)地提供CGI生成的頁(yè)面,除非有人(或者是一個(gè)cronjob)刪除了這些靜態(tài)頁(yè)面,而且其內容可以得到更新。
RewriteCond %{REQUEST_FILENAME} !-s
RewriteRule ^page\.html$ page.cgi [T=application/x-httpd-cgi,L]
這樣,如果page.html不存在或者文件大小為null,則對page.html的請求會(huì )導致page.cgi的運行。其中奧妙在于,page.cgi是一個(gè)將輸出寫(xiě)入page.html的(同時(shí)也寫(xiě)入STDOUT)的常規的CGI腳本,執行完畢,服務(wù)器則將page.html的內容發(fā)出。如果網(wǎng)管需要強制更新其內容,只須刪除page.html即可(通常由一個(gè)cronjob完成)。
建立一個(gè)復雜的頁(yè)面,能夠在用編輯器寫(xiě)了一個(gè)更新的版本時(shí)自動(dòng)在瀏覽器上得到刷新,這不是很好嗎?這可能嗎?
這是可行的! 這需要綜合利用MIME多成分、網(wǎng)站服務(wù)器的NPH和mod_rewrite的URL操控特性。首先,建立一個(gè)新的URL特性:對在文件系統中更新時(shí)需要刷新的所有URL加上:refresh。
RewriteRule ^(/[uge]/[^/]+/?.*):refresh /internal/cgi/apache/nph-refresh?f=$1
然后,修改URL
/u/foo/bar/page.html:refresh
以?xún)炔康夭倏卮薝RL
/internal/cgi/apache/nph-refresh?f=/u/foo/bar/page.html
接著(zhù)就是NPH-CGI腳本部分了。雖然,人們常說(shuō)"left as an exercise to the reader";-),我還是給出答案了。
#!/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>功能很強,在有幾十個(gè)虛擬主機的情況下運行得很好,但是如果你是ISP,需要提供幾百個(gè)虛擬主機,那么這就不是一個(gè)最佳的選擇了。
為此,需要用代理吞吐(Proxy Throughput)功能(flag [P])映射遠程頁(yè)面甚至整個(gè)遠程網(wǎng)絡(luò )區域到自己的名稱(chēng)空間:
## ## 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}}]
:
如何阻止一個(gè)完全匿名的robot取得特定網(wǎng)絡(luò )區域的頁(yè)面?一個(gè)/robots.txt文件可以包含若干"Robot Exclusion Protocol(robot排除協(xié)議)"的行,但不足以阻止此類(lèi)robot。
可以用一個(gè)規則集以拒絕對網(wǎng)絡(luò )區域/~quux/foo/arc/(對一個(gè)很深的目錄區域進(jìn)行列表可能會(huì )使服務(wù)器產(chǎn)生很大的負載)的訪(fǎng)問(wèn)。還必須確保僅阻止特定的robot,就是說(shuō),僅僅阻止robot訪(fǎng)問(wèn)主機是不夠的,這樣會(huì )同時(shí)也阻止了用戶(hù)訪(fǎng)問(wèn)該主機。為此,就需要對HTTP頭的User-Agent信息作匹配。
RewriteCond %{HTTP_USER_AGENT} ^NameOfBadRobot.*
RewriteCond %{REMOTE_ADDR} ^123\.45\.67\.[8-9]$
RewriteRule ^/~quux/foo/arc/.+ - [F]
假設,http://www.quux-corp.de/~quux/有一些內嵌圖片的頁(yè)面,這些圖片很好,所以就有人用超鏈連到他們自己的頁(yè)面中了。由于這樣徒然增加了我們的服務(wù)器的流量,因此,我們不愿意這種事情發(fā)生。
雖然,我們不能100%地保護這些圖片不被寫(xiě)入別人的頁(yè)面,但至少可以對發(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]
如何拒絕一批外部列表中的主機對我們服務(wù)器的使用?
For Apache >= 1.3b6:
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]
For Apache <= 1.3b6:
RewriteEngine on
RewriteMap hosts-deny txt:/path/to/hosts.deny
RewriteRule ^/(.*)$ ${hosts-deny:%{REMOTE_HOST}|NOT-FOUND}/$1
RewriteRule !^NOT-FOUND/.* - [F]
RewriteRule ^NOT-FOUND/(.*)$ ${hosts-deny:%{REMOTE_ADDR}|NOT-FOUND}/$1
RewriteRule !^NOT-FOUND/.* - [F]
RewriteRule ^NOT-FOUND/(.*)$ /$1
## ## hosts.deny ## ## ATTENTION! This is a map, not a list, even when we treat it as such. ## mod_rewrite parses it for key/value pairs, so at least a ## dummy value "-" must be present for each entry. ## 193.102.180.41 - bsdti1.sdm.de - 192.76.162.40 -
如何拒絕某個(gè)主機或者來(lái)自特定主機的用戶(hù)使用Apache代理?
首先,要確保Apache網(wǎng)站服務(wù)器在編譯時(shí)配置文件中mod_rewrite在mod_proxy的下面(!),使它在mod_proxy之前被調用。然后,如下拒絕某個(gè)主機...
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]
有時(shí)候,會(huì )需要一種非常特殊的認證,即,對一組明確指定的用戶(hù),允許其訪(fǎng)問(wèn),而沒(méi)有(在使用mod_access的基本認證方法時(shí)可能會(huì )出現的)任何提示。
可是使用一個(gè)重寫(xiě)條件列表來(lái)排除所有的朋友:
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]
如何配置一個(gè)基于HTTP頭"Referer"的反射器以反射到任意數量的提交頁(yè)面?
使用這個(gè)很技巧的規則集...
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]
... 并結合對應的重寫(xiě)地圖:
## ## 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/
它可以自動(dòng)將請求(在地圖中指定了"-"值的時(shí)候)反射回其提交頁(yè)面,或者(在地圖中URL有第二個(gè)參數時(shí))反射到一個(gè)特定的URL。
一個(gè)常見(jiàn)的問(wèn)題: 如何解決似乎無(wú)法用mod_rewrite解決的FOO/BAR/QUUX/之類(lèi)的問(wèn)題?
可以使用一個(gè)與RewriteMap功能相同的外部RewriteMap程序,一旦它在A(yíng)pache啟動(dòng)時(shí)被執行,則從STDIN接收被請求的URL,并將處理過(guò)(通常是重寫(xiě)過(guò)的)的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 $_;
}
這是一個(gè)作演示的例子,只是把所有的URL /~quux/foo/...重寫(xiě)為/~quux/bar/...,而事實(shí)上,可以把它修改以獲得任何你需要的功能。但是要注意,雖然一般用戶(hù)都可以使用,可是只有系統管理員才可以定義這樣的地圖。
聯(lián)系客服