亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

<-
Apache > HTTP Server > 文檔 > 版本2.2 > 雜項(xiàng)文檔
   致謝 | 譯者聲明 | 本篇譯者:金步國(guó) | 本篇譯稿最后更新:2006年1月11日 | 獲取最新版本

URL重寫(xiě)指南

Originally written by
Ralf S. Engelschall <rse@apache.org>
December 1997

本文是mod_rewrite參考文檔,闡述在實(shí)際應(yīng)用中如何解決網(wǎng)管所面臨的基于URL的典型問(wèn)題,并詳細(xì)描述了如何配置URL重寫(xiě)規(guī)則集以解決問(wèn)題。

top

mod_rewrite簡(jiǎn)介

Apache的mod_rewrite是提供了強(qiáng)大URL操作的殺手級(jí)的模塊,可以實(shí)現(xiàn)幾乎所有你夢(mèng)想的URL操作類型,其代價(jià)是你必須接受其復(fù)雜性,因?yàn)?code class="module">mod_rewrite的主要障礙就是初學(xué)者不容易理解和運(yùn)用,即使是Apache專家有時(shí)也會(huì)發(fā)掘出mod_rewrite的新用途。換句話說(shuō):對(duì)于mod_rewrite,或者是打退堂鼓永不再用,或者是喜歡它并一生受用。本文試圖通過(guò)對(duì)已有方案的表述來(lái)創(chuàng)造一個(gè)成功的開(kāi)端,以免你放棄。

top

實(shí)踐方案

我自己創(chuàng)造和收集了許多實(shí)踐方案,不要有畏懼心理,從這些例子開(kāi)始學(xué)習(xí)URL重寫(xiě)的黑匣子吧。

注意:根據(jù)你的服務(wù)器配置,可能有必要對(duì)例子作些微修改,比如,新啟用mod_aliasmod_userdir時(shí)要增加[PT]標(biāo)志,或者重寫(xiě).htaccess而不是單個(gè)服務(wù)器中的規(guī)則集。對(duì)一個(gè)特定的規(guī)則集應(yīng)該先透徹理解然后再考慮應(yīng)用,這樣才能避免出現(xiàn)問(wèn)題。
top

URL 的規(guī)劃

規(guī)范的URL

說(shuō)明:

在有些web服務(wù)器上,一個(gè)資源會(huì)擁有多個(gè)URL,在實(shí)際應(yīng)用和發(fā)布中應(yīng)該被使用的是規(guī)范的URL,其他的則是簡(jiǎn)寫(xiě)或者只在內(nèi)部使用。無(wú)論用戶在請(qǐng)求中使用什么形式的URL,他最終看見(jiàn)的都應(yīng)該是規(guī)范的URL。

方案:

對(duì)所有不規(guī)范的URL執(zhí)行一個(gè)外部HTTP重定向,以改變它在瀏覽器地址欄中的顯示及其后繼請(qǐng)求。下例中的規(guī)則集用規(guī)范的/u/user替換/~user,并修正了/u/user所遺漏的后綴斜杠。

RewriteRule   ^/~([^/]+)/?(.*)    /u/$1/$2  [R]
RewriteRule   ^/([uge])/([^/]+)$  /$1/$2/   [R]

規(guī)范的主機(jī)名

說(shuō)明:
這個(gè)規(guī)則的目的是強(qiáng)制使用特定的主機(jī)名以代替其他名字。比如,你想強(qiáng)制使用www.example.com代替example.com,就可以在以下方法的基礎(chǔ)上進(jìn)行修改:
方案:
# 針對(duì)運(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]

# 對(duì)一個(gè)運(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]

移動(dòng)過(guò)的DocumentRoot

說(shuō)明:

通常,web服務(wù)器的DocumentRoot直接對(duì)應(yīng)于URL"/",但是,它常常不是處于最高一級(jí),而可能只是眾多數(shù)據(jù)池中的一個(gè)實(shí)體。比如,在Intranet站點(diǎn)中,有/e/www/(WWW的主頁(yè))、/e/sww/(Intranet的主頁(yè))等等,而DocumentRoot指向了/e/www/,則必須保證此數(shù)據(jù)池中所有內(nèi)嵌的圖片和其他元素對(duì)后繼請(qǐng)求有效。

方案:

只須重定向URL"/"到"/e/www/"即可。這個(gè)方案看起來(lái)很簡(jiǎn)單,但只是因?yàn)橛辛薽od_rewrite模塊的支持,它才簡(jiǎn)單,因?yàn)閭鹘y(tǒng)的URL Aliases機(jī)制(由mod_alias及其相關(guān)模塊提供)只是作了一個(gè)前綴匹配,DocumentRoot是一個(gè)對(duì)所有URL的前綴,因而無(wú)法實(shí)現(xiàn)這樣的重定向。而用mod_rewrite的確很簡(jiǎn)單:

RewriteEngine on
RewriteRule   ^/$  /e/www/  [R]

注意, 也可以通過(guò)RedirectMatch指令達(dá)到這個(gè)目的:

RedirectMatch ^/$ http://example.com/e/www/

后綴斜杠的問(wèn)題

說(shuō)明:

每個(gè)網(wǎng)管對(duì)引用目錄后綴斜杠的問(wèn)題都有一本苦經(jīng),如果遺漏了,服務(wù)器會(huì)產(chǎn)生一個(gè)錯(cuò)誤,因?yàn)槿绻?qǐng)求是/~quux/foo而不是/~quux/foo/ ,服務(wù)器就會(huì)去找一個(gè)叫foo的文件,而它是一個(gè)目錄,所以就報(bào)錯(cuò)了。事實(shí)上,大多數(shù)情況下,它自己會(huì)試圖修正這個(gè)錯(cuò)誤,但是有時(shí)候需要你手工糾正,比如,在重寫(xiě)了許多CGI腳本中的復(fù)雜的URL以后。

方案:

解決這個(gè)微妙問(wèn)題的方案是讓服務(wù)器自動(dòng)添加后綴斜杠。對(duì)此,必須使用一個(gè)外部重定向,使瀏覽器正確地處理后繼的對(duì)諸如圖片的請(qǐng)求。如果僅僅作一個(gè)內(nèi)部重寫(xiě),可能只對(duì)目錄頁(yè)面有效,而對(duì)內(nèi)嵌有使用相對(duì)URL的圖片的頁(yè)面無(wú)效,因?yàn)闉g覽器有請(qǐng)求內(nèi)嵌目標(biāo)的可能。比如,如果不用外部重定向,/~quux/foo/index.html頁(yè)面中對(duì)image.gif的請(qǐng)求,其結(jié)果將是/~quux/image.gif

所以,應(yīng)該這樣寫(xiě):

RewriteEngine  on
RewriteBase    /~quux/
RewriteRule    ^foo$  foo/  [R]

又懶又瘋狂的做法是把這些寫(xiě)入其宿主目錄中的頂級(jí).htaccess中,但是須注意,如此會(huì)帶來(lái)一些處理上的開(kāi)銷。

RewriteEngine  on
RewriteBase    /~quux/
RewriteCond    %{REQUEST_FILENAME}  -d
RewriteRule    ^(.+[^/])$           $1/  [R]

集群網(wǎng)站的同類URL規(guī)劃

說(shuō)明:

我們希望在一個(gè)Intranet集群網(wǎng)站中,對(duì)所有WWW服務(wù)器建立一致的URL規(guī)劃,也就是說(shuō),所有的URL(針對(duì)每個(gè)服務(wù)器進(jìn)行本地配置,因此是獨(dú)立于各個(gè)服務(wù)器的)實(shí)際上都是獨(dú)立于各個(gè)服務(wù)器的!我們需要的是一個(gè)具有獨(dú)立于各個(gè)服務(wù)器的一致性規(guī)劃的WWW名稱空間,即URL不需要包含物理目標(biāo)服務(wù)器,而由集群本身來(lái)自動(dòng)定位物理目標(biāo)主機(jī)。

方案:

首先,目標(biāo)服務(wù)器的信息來(lái)自(產(chǎn)生)于包含有用戶、組以及實(shí)體的外部地圖,其格式形如:

user1  server_of_user1
user2  server_of_user2
:      :

這些信息被存入map.xxx-to-host文件。其次,如果URL在一個(gè)服務(wù)器上無(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ī)則集依靠映射文件來(lái)完成這個(gè)操作(假定,如果一個(gè)用戶在映射中沒(méi)有對(duì)應(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\

移動(dòng)用戶主目錄到不同的web服務(wù)器

說(shuō)明:

通常,許多網(wǎng)管在建立一個(gè)新的web服務(wù)器時(shí),都會(huì)有這樣的要求:重定向一個(gè)web服務(wù)器上的所有用戶主目錄到另一個(gè)web服務(wù)器。

方案:

很簡(jiǎn)單,在老的web服務(wù)器上重定向所有的URL"/~user/anypath"到http://newserver/~user/anypath

RewriteEngine on
RewriteRule   ^/~(.+)  http://newserver/~$1  [R,L]

結(jié)構(gòu)化的用戶主目錄

說(shuō)明:

一些擁有幾千個(gè)用戶的網(wǎng)站通常都使用結(jié)構(gòu)化的用戶主目錄規(guī)劃,即每個(gè)用戶主目錄位于一個(gè)帶有特定前綴,比如其用戶名的第一個(gè)字符的子目錄下:/~foo/anypath代表/home/f/foo/.www/anypath,而/~bar/anypath代表/home/b/bar/.www/anypath

方案:

可以使用下列規(guī)則集來(lái)擴(kuò)展~以達(dá)到上述目的。

RewriteEngine on
RewriteRule   ^/~(([a-z])[a-z0-9]+)(.*)  /home/$2/$1/.www$3

文件系統(tǒng)的重組

說(shuō)明:

這是一個(gè)不加雕琢的例子:一個(gè)大量使用針對(duì)目錄的規(guī)則集以實(shí)現(xiàn)平滑的觀感,并且從來(lái)不用調(diào)整數(shù)據(jù)結(jié)構(gòu)的殺手級(jí)的應(yīng)用。背景:net.sw從1992年開(kāi)始,存放了我收集的免費(fèi)Unix軟件包。它是我的愛(ài)好也是我的工作,因?yàn)樵趯W(xué)習(xí)計(jì)算機(jī)科學(xué)的同時(shí),業(yè)余時(shí)間還做了多年的系統(tǒng)和網(wǎng)絡(luò)管理員。每周我都需要整理軟件,因而建立了一個(gè)層次很深的目錄結(jié)構(gòu)來(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è)目錄結(jié)構(gòu),同時(shí)不對(duì)這個(gè)結(jié)構(gòu)做任何改變,甚至也不在結(jié)構(gòu)頂部放置CGI腳本。為什么呢?因?yàn)檫@個(gè)結(jié)構(gòu)還要能夠被FTP訪問(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"子目錄包含了上述目錄結(jié)構(gòu),即實(shí)在的net.sw ,由rdist在需要的時(shí)候自動(dòng)更新。第二個(gè)部分的遺留問(wèn)題是:如何連接這兩個(gè)結(jié)構(gòu)為一個(gè)平滑觀感的URL樹(shù)?我希望在運(yùn)行適當(dāng)?shù)腃GI腳本而使用各種URL的時(shí)候,使用戶感覺(jué)不到"DATA"目錄的存在。方案如下:首先,我把下列配置放在服務(wù)器上DocumentRoot中針對(duì)目錄的配置文件里,重寫(xiě)公布的URL"/net.sw/"為內(nèi)部路徑"/e/netsw" :

RewriteRule  ^net.sw$       net.sw/        [R]
RewriteRule  ^net.sw/(.*)$  e/netsw/$1

第一條規(guī)則是針對(duì)遺漏后綴斜杠的請(qǐng)求的!第二條規(guī)則才是真正實(shí)現(xiàn)功能的。接著,就是放在針對(duì)目錄的配置文件/e/netsw/.www/.wwwacl中的殺手級(jí)的配置了:

Options       ExecCGI FollowSymLinks Includes MultiViews

RewriteEngine on

# 我們通過(guò)"/net.sw/"前綴到達(dá)
RewriteBase   /net.sw/

# 首先重寫(xiě)根目錄到cgi處理腳本
RewriteRule   ^$                       netsw-home.cgi     [L]
RewriteRule   ^index\.html$            netsw-home.cgi     [L]

#  當(dāng)瀏覽器請(qǐng)求perdir頁(yè)面時(shí)剝?nèi)プ幽夸?
RewriteRule   ^.+/(netsw-[^/]+/.+)$    $1                 [L]

# 現(xiàn)在打斷本地文件的重寫(xiě)
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]

# 任何別的東西都是一個(gè)由另一個(gè)cgi腳本處理的子目錄
RewriteRule   !^netsw-lsdir\.cgi.*     -                  [C]
RewriteRule   (.*)                     netsw-lsdir.cgi/$1

閱讀提示:

  1. 注意前半部分中的標(biāo)志L(最后),和無(wú)對(duì)應(yīng)項(xiàng)("-")
  2. 注意后半部分中的符號(hào)!(非),和標(biāo)志C(鏈)
  3. 注意最后一條規(guī)則的全匹配模式

NCSA圖像映射和mod_imap

說(shuō)明:

許多人都希望在從NCSA web服務(wù)器向較現(xiàn)代的Apache web服務(wù)器轉(zhuǎn)移中實(shí)現(xiàn)平滑過(guò)渡,即希望老的NCSA圖像映射程序能在Apache的較現(xiàn)代的mod_imap支持下正常運(yùn)作。但問(wèn)題在于,到處都是通過(guò)/cgi-bin/imagemap/path/to/page.map引用imagemap程序的連接,而在Apache下,應(yīng)該寫(xiě)成/path/to/page.map

方案:

使用全局規(guī)則在傳輸過(guò)程中去除所有這些請(qǐng)求的前綴:

RewriteEngine  on
RewriteRule    ^/cgi-bin/imagemap(.*)  $1  [PT]

在多個(gè)目錄中搜索頁(yè)面

說(shuō)明:

有時(shí)會(huì)有必要使web服務(wù)器在多個(gè)目錄中搜索頁(yè)面,對(duì)此,MultiViews或者其他技術(shù)無(wú)能為力。

方案:

編制一個(gè)明確的規(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]

按照URL的片段設(shè)置環(huán)境變量

說(shuō)明:

如果希望保持請(qǐng)求之間的狀態(tài)信息,又不希望使用CGI來(lái)包裝所有頁(yè)面,而是只通過(guò)分離URL中的有用信息來(lái)編碼。

方案:

可以用一個(gè)規(guī)則集來(lái)分離出狀態(tài)信息,并設(shè)置環(huán)境變量以備此后用于XSSI或CGI 。這樣,一個(gè)"/foo/S=java/bar/"的URL會(huì)被解析為/foo/bar/ ,而環(huán)境變量STATUS則被設(shè)置為"java"。

RewriteEngine on
RewriteRule   ^(.*)/S=([^/]+)/(.*)    $1/$3 [E=STATUS:$2]

虛擬用戶主機(jī)

說(shuō)明:

如果需要為用戶username支持一個(gè)www.username.host.domain.com的主頁(yè),但不是用在此機(jī)器上建虛擬主機(jī)的方法,而是用僅在此機(jī)器上增加一個(gè)DNS記錄的方法實(shí)現(xiàn)。

方案:

對(duì)HTTP/1.0的請(qǐng)求,這是無(wú)法實(shí)現(xiàn)的;但是對(duì)HTTP/1.1的在HTTP頭中包含有主機(jī)名的請(qǐng)求,可以用以下規(guī)則集來(lái)內(nè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)訪問(wèn)者重定向用戶主目錄

說(shuō)明:

對(duì)不是來(lái)自本地域ourdomain.com的外來(lái)訪問(wèn)者的請(qǐng)求,重定向其用戶主目錄URL到另一個(gè)web服務(wù)器www.somewhere.com ,有時(shí)這種做法也會(huì)用在虛擬主機(jī)的配置段中。

方案:

只須一個(gè)重寫(xiě)條件:

RewriteEngine on
RewriteCond   %{REMOTE_HOST}  !^.+\.ourdomain\.com$
RewriteRule   ^(/~.+)         http://www.somewhere.com/$1 [R,L]

重定向失敗的URL到其他web服務(wù)器

說(shuō)明:

如何重寫(xiě)URL以重定向?qū)eb服務(wù)器A的失敗請(qǐng)求到服務(wù)器B,是一個(gè)常見(jiàn)的問(wèn)題。一般,可以用Perl寫(xiě)的CGI腳本通過(guò)ErrorDocument來(lái)解決,此外,還有mod_rewrite方案。但是須注意,這種方法的執(zhí)行效率不如用ErrorDocument的CGI腳本!

方案:

第一種方案,有最好的性能而靈活性欠佳,出錯(cuò)概率小所以安全:

RewriteEngine on
RewriteCond   /your/docroot/%{REQUEST_FILENAME} !-f
RewriteRule   ^(.+)                             http://webserverB.dom/$1

但是其問(wèn)題在于,它只對(duì)位于DocumentRoot中的頁(yè)面有效。雖然可以增加更多的條件(比如同時(shí)還處理用戶主目錄,等等),但是還有一個(gè)更好的方法:

RewriteEngine on
RewriteCond   %{REQUEST_URI} !-U
RewriteRule   ^(.+)          http://webserverB.dom/$1

這種方法使用了mod_rewrite提供的"向前參照"(look-ahead)的功能,是一種對(duì)所有URL類型都有效而且安全的方法。但是,對(duì)web服務(wù)器的性能會(huì)有影響,所以如果web服務(wù)器有一個(gè)強(qiáng)大的CPU,那就用這個(gè)方法。而在慢速機(jī)器上,可以用第一種方法,或者用性能更好的ErrorDocumentCGI腳本。

擴(kuò)展的重定向

說(shuō)明:

有時(shí)候,我們會(huì)需要更多的對(duì)重定向URL的(有關(guān)字符轉(zhuǎn)義機(jī)制方面的)控制。通常,Apache內(nèi)核中的URL轉(zhuǎn)義函數(shù)uri_escape()同時(shí)還會(huì)對(duì)錨(anchor)轉(zhuǎn)義,即類似"url#anchor"的URL,因此,你不能用mod_rewrite對(duì)此類URL直接重定向。那么如何實(shí)現(xiàn)呢?

方案:

必須用NPH-CGI腳本使它自己重定向,因?yàn)閷?duì)NPH(無(wú)須解析的HTTP頭)不會(huì)發(fā)生轉(zhuǎn)義操作。首先,在針對(duì)服務(wù)器的配置中(應(yīng)該位于所有重寫(xiě)規(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
注意:無(wú)須對(duì)上述規(guī)則加[R]或[R,L],因?yàn)?quot;xredirect:"需要在稍后被其特殊的"管道傳送"規(guī)則擴(kuò)展。

文檔訪問(wèn)的多路復(fù)用

說(shuō)明:

你知道http://www.perl.com/CPAN的CPAN(綜合Perl存檔網(wǎng)絡(luò))?它實(shí)現(xiàn)了一個(gè)重定向以提供全世界的CPAN鏡像中離訪問(wèn)者最近的一個(gè)FTP站點(diǎn),也可以稱之為FTP訪問(wèn)多路復(fù)用服務(wù)。CPAN是通過(guò)CGI腳本實(shí)現(xiàn)的,那么用mod_rewrite如何實(shí)現(xiàn)呢?

方案:

首先,我們注意到mod_rewrite從3.0.0版本開(kāi)始,還可以重寫(xiě)"ftp:"類型。其次,對(duì)客戶端頂級(jí)域名的路徑最近的求取可以用RewriteMap實(shí)現(xiàn)。利用鏈?zhǔn)揭?guī)則集,并用頂級(jí)域名作為查找多路復(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##

依賴于時(shí)間的重寫(xiě)

說(shuō)明:

在頁(yè)面內(nèi)容按時(shí)間不同而變化的場(chǎng)合,比如重定向特定頁(yè)面,許多網(wǎng)管仍然采用CGI腳本的方法,如何用mod_rewrite來(lái)實(shí)現(xiàn)呢?

方案:

有許多類似TIME_xxx的變量可以用在重寫(xiě)條件中,利用"<STRING", " >STRING"和"=STRING"的類型比較,并加以連接,就可以實(shí)現(xiàn)依賴于時(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

此例使URLfoo.html在07:00-19:00時(shí)指向foo.day.html ,而在其余時(shí)間,則指向foo.night.html ,對(duì)主頁(yè)是一個(gè)不錯(cuò)的功能...

對(duì)YYYY過(guò)渡為XXXX的向前兼容

說(shuō)明:

在轉(zhuǎn)變了大批.html文件為.phtml ,使文檔.YYYY過(guò)渡成為文檔.XXXX后,如何保持URL的向前兼容(仍然虛擬地存在)?

方案:

只須按基準(zhǔn)文件名重寫(xiě),并測(cè)試帶有新的擴(kuò)展名的文件是否存在,如果存在,則用新的,否則,仍然用原來(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
top

內(nèi)容的處理

新舊URL(內(nèi)部的)

說(shuō)明:

假定已經(jīng)把文件bar.html改名為foo.html ,需要對(duì)老的URL向前兼容,即讓用戶仍然可以使用老的URL,而感覺(jué)不到文件被改名了。

方案:

通過(guò)以下規(guī)則內(nèi)部地重寫(xiě)老的URL為新的:

RewriteEngine  on
RewriteBase    /~quux/
RewriteRule    ^foo\.html$  bar.html

新舊URL(外部的)

說(shuō)明:

仍然假定已經(jīng)把文件bar.html改名為foo.html ,需要對(duì)老的URL向前兼容,但是要讓用戶得到文件被改名的暗示,即瀏覽器的地址欄中顯示的是新的URL。

方案:

作一個(gè)HTTP的強(qiáng)制重定向以改變?yōu)g覽器和用戶界面上的顯示:

RewriteEngine  on
RewriteBase    /~quux/
RewriteRule    ^foo\.html$  bar.html  [R]

依賴于瀏覽器的內(nèi)容

說(shuō)明:

至少對(duì)重要的頂級(jí)頁(yè)面,有時(shí)候有必要提供依賴于瀏覽器的最佳的內(nèi)容,即對(duì)最新的Netscape提供最大化的版本,對(duì)Lynx提供最小化的版本,而對(duì)其他的瀏覽器則提供一個(gè)功能一般的版本。

方案:

對(duì)此,內(nèi)容協(xié)商無(wú)能為力,因?yàn)闉g覽器不提供那種形式的類型,所以只能在HTTP頭"User-Agent"上想辦法。以下規(guī)則集可以完成這個(gè)操作:如果HTTP頭"User-Agent"以"Mozilla/3"開(kāi)頭,則頁(yè)面foo.html被重寫(xiě)為foo.NS.html ,而后重寫(xiě)操作終止;如果是"Lynx"或者版本號(hào)為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]

動(dòng)態(tài)鏡像

說(shuō)明:

假定,需要在我們的名稱空間里加入其他遠(yuǎn)程主機(jī)的頁(yè)面。對(duì)FTP服務(wù)器,可以用mirror程序以在本地機(jī)器上維持一個(gè)對(duì)遠(yuǎn)程數(shù)據(jù)的最新的拷貝;對(duì)web服務(wù)器,可以用類似的用于HTTP的webcopy程序。但這兩種技術(shù)都有一個(gè)主要的缺點(diǎn):此本地拷貝必須通過(guò)這個(gè)程序的執(zhí)行來(lái)更新。所以,比較好的方法是,不采用靜態(tài)鏡像,而采用動(dòng)態(tài)鏡像,即在有數(shù)據(jù)請(qǐng)求時(shí)自動(dòng)更新(遠(yuǎn)程主機(jī)上更新的數(shù)據(jù))。

方案:

為此,使用代理吞吐(Proxy Throughput)功能(flag [P]),以映射遠(yuǎn)程頁(yè)面甚至整個(gè)遠(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]

反向動(dòng)態(tài)鏡像

說(shuō)明:
...
方案:
RewriteEngine on
RewriteCond   /mirror/of/remotesite/$1           -U
RewriteRule   ^http://www\.remotesite\.com/(.*)$ /mirror/of/remotesite/$1

通過(guò)Intranet取得丟失的數(shù)據(jù)

說(shuō)明:

這是一種在受防火墻保護(hù)的(內(nèi)部)Intranet(www2.quux-corp.dom)上保存和維護(hù)實(shí)際數(shù)據(jù),而虛擬地運(yùn)行企業(yè)級(jí)(外部)Internet web服務(wù)器(www.quux-corp.dom)的巧妙的方法。這種方法是外部服務(wù)器在空閑時(shí)間從內(nèi)部服務(wù)器取得被請(qǐng)求的數(shù)據(jù)。

方案:

首先,必須確保防火墻對(duì)內(nèi)部服務(wù)器的保護(hù),并只允許此外部服務(wù)器取得數(shù)據(jù)。對(duì)包過(guò)濾(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í)際配置,只要對(duì)上例稍作調(diào)整即可。接著,建立通過(guò)代理后臺(tái)獲取丟失數(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]

負(fù)載的均衡

說(shuō)明:

如何均衡www.foo.com的負(fù)載到www[0-5].foo.com(一共是6個(gè)服務(wù)器)?

方案:

這個(gè)問(wèn)題有許多可能的解決方案,在此,我們討論通稱為“基于DNS”的方案,和特殊的使用mod_rewrite的方案:

  1. DNS循環(huán)(DNS Round-Robin)

    最簡(jiǎn)單的方法是用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.
    

    注意,上述看起來(lái)似乎是錯(cuò)誤的,但事實(shí)上,它的確是BIND中的一個(gè)預(yù)期的特性,而且也可以這樣用。無(wú)論如何,現(xiàn)在www.foo.com已經(jīng)被解析,BIND可以給出www0-www6 ,雖然每次在次序上會(huì)有輕微的置換/循環(huán),客戶端的請(qǐng)求可以被分散到各個(gè)服務(wù)器。但這并不是一個(gè)優(yōu)秀的負(fù)載均衡方案,因?yàn)镈NS解析信息可以被網(wǎng)絡(luò)中其他名稱服務(wù)器緩沖,而一旦www.foo.com被解析為wwwN.foo.com,則其后繼請(qǐng)求都將被送往www.foo.com。但是最終結(jié)果是正確的,因?yàn)檎?qǐng)求的總量的確被分散到各個(gè)服務(wù)器了

  2. DNS 負(fù)載均衡

    一種成熟的基于DNS的負(fù)載均衡方法是使用http://www.stanford.edu/~schemers/docs/lbnamed/lbnamed.htmllbnamed程序,它是一個(gè)Perl5程序,帶有若干輔助工具,實(shí)現(xiàn)了真正的基于DNS的負(fù)載均衡。

  3. 代理吞吐循環(huán)(Proxy Throughput Round-Robin)

    這是一個(gè)使用mod_rewrite及其代理吞吐特性的方法。首先,在DNS記錄中將www0.foo.com固定為www.foo.com ,如下:

    www    IN  CNAME   www0.foo.com.
    

    其次,將www0.foo.com轉(zhuǎn)換為一個(gè)專職代理服務(wù)器,即由這個(gè)機(jī)器把所有到來(lái)的URL通過(guò)內(nèi)部代理分散到另外5個(gè)服務(wù)器(www1-www5)。為此,必須建立一個(gè)規(guī)則集,對(duì)所有URL調(diào)用一個(gè)負(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##
    
    最后的說(shuō)明:這樣有用嗎?www0.foo.com似乎也會(huì)超載呀?答案是:沒(méi)錯(cuò),它的確會(huì)超載,但是它超載的僅僅是簡(jiǎn)單的代理吞吐請(qǐng)求!所有諸如SSI、CGI、ePerl等等的處理完全是由其他機(jī)器完成的,這個(gè)才是要點(diǎn)。
  4. 硬件/TCP循環(huán)

    還有一個(gè)硬件解決方案。Cisco有一個(gè)叫LocalDirector的東西,實(shí)現(xiàn)了TCP/IP層的負(fù)載均衡,事實(shí)上,它是一個(gè)位于網(wǎng)站集群前端的電路級(jí)網(wǎng)關(guān)。如果你有足夠資金而且的確需要高性能的解決方案,那么可以用這個(gè)。

反向代理

說(shuō)明:
...
方案:
##
##  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

新的MIME類型,新的服務(wù)

說(shuō)明:

網(wǎng)上有許多很巧妙的CGI程序,但是用法晦澀,許多網(wǎng)管棄之不用。即使是Apache的MEME類型的動(dòng)作處理器,也僅僅在CGI程序不需要在其輸入中包含特殊URL(PATH_INFOQUERY_STRINGS)時(shí)才很好用。首先,配置一種新的后綴為.scgi(安全CGI)文件類型,其處理器是很常見(jiàn)的cgiwrap程序。問(wèn)題是:如果使用同類URL規(guī)劃(見(jiàn)上述),而用戶宿主目錄中的一個(gè)文件的URL是/u/user/foo/bar.scgi ,可是cgiwrap要求的URL的格式是/~user/foo/bar.scgi/ ,以下規(guī)則解決了這個(gè)問(wèn)題:

RewriteRule ^/[uge]/([^/]+)/\.www/(.+)\.scgi(.*) ...
... /internal/cgi/user/cgiwrap/~$1/$2.scgi$3  [NS,T=application/x-http-cgi]

另外,假設(shè)需要使用其他程序:wwwlog(顯示access.log中的一個(gè)URL子樹(shù))和wwwidx(對(duì)一個(gè)URL子樹(shù)運(yùn)行Glimpse),則必須對(duì)這些程序提供URL區(qū)域作為其操作對(duì)象。比如,對(duì)/u/user/foo/執(zhí)行swwidx程序的超鏈?zhǔn)沁@樣的:

/internal/cgi/user/swwidx?i=/u/user/foo/

其缺點(diǎn)是,必須同時(shí)硬編碼超鏈中的區(qū)域和CGI的路徑,如果重組了這個(gè)區(qū)域,就需要花費(fèi)大量時(shí)間來(lái)修改各個(gè)超鏈。

方案:

方案是用一個(gè)特殊的新的URL格式,自動(dòng)拼裝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)在,這個(gè)搜索到/u/user/foo/的超鏈簡(jiǎn)化成了:

HREF="*"

它會(huì)被內(nèi)部地自動(dòng)轉(zhuǎn)換為

/internal/cgi/user/wwwidx?i=/u/user/foo/

如此,可以為使用":log"的超鏈,拼裝出調(diào)用CGI程序的參數(shù)。

從靜態(tài)到動(dòng)態(tài)

說(shuō)明:

如何無(wú)縫轉(zhuǎn)換靜態(tài)頁(yè)面foo.html為動(dòng)態(tài)的foo.cgi ,而不為瀏覽器/用戶所察覺(jué)。

方案:

只須重寫(xiě)此URL為CGI-script ,以強(qiáng)制為可以作為CGI-script運(yùn)行的正確的MIME類型。如此,對(duì)/~quux/foo.html的請(qǐng)求其實(shí)會(huì)執(zhí)行/~quux/foo.cgi 。

RewriteEngine  on
RewriteBase    /~quux/
RewriteRule    ^foo\.html$  foo.cgi  [T=application/x-httpd-cgi]

傳輸過(guò)程中的內(nèi)容協(xié)商

說(shuō)明:

這是一個(gè)很難解的功能:動(dòng)態(tài)生成的靜態(tài)頁(yè)面,即它應(yīng)該作為靜態(tài)頁(yè)面發(fā)送(從文件系統(tǒng)中讀出,然后直接發(fā)出去),但是如果它丟失了,則由服務(wù)器動(dòng)態(tài)生成。這樣,可以靜態(tài)地提供CGI生成的頁(yè)面,除非有人(或者是一個(gè)cronjob)刪除了這些靜態(tài)頁(yè)面,而且其內(nèi)容可以得到更新。

方案:
以下規(guī)則集實(shí)現(xiàn)了這個(gè)功能:
RewriteCond %{REQUEST_FILENAME}   !-s
RewriteRule ^page\.html$          page.cgi   [T=application/x-httpd-cgi,L]

這樣,如果page.html不存在或者文件大小為null ,則對(duì)page.html的請(qǐng)求會(huì)導(dǎo)致page.cgi的運(yùn)行。其中奧妙在于page.cgi是一個(gè)將輸出寫(xiě)入page.html的(同時(shí)也寫(xiě)入STDOUT)的常規(guī)的CGI腳本,執(zhí)行完畢,服務(wù)器則將page.html的內(nèi)容發(fā)出。如果網(wǎng)管需要強(qiáng)制更新其內(nèi)容,只須刪除page.html即可(通常由一個(gè)cronjob完成)。

自動(dòng)更新的文檔

說(shuō)明:

建立一個(gè)復(fù)雜的頁(yè)面,能夠在用編輯器寫(xiě)了一個(gè)更新的版本時(shí)自動(dòng)在瀏覽器上得到刷新,這不是很好嗎?這可能嗎?

方案:

這是可行的! 這需要綜合利用MIME多成分、web服務(wù)器的NPH和mod_rewrite的URL操控特性。首先,建立一個(gè)新的URL特性:對(duì)在文件系統(tǒng)中更新時(shí)需要刷新的所有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腳本部分了。雖然,人們常說(shuō)"將此作為一個(gè)練習(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 "&lt;b&gt;ERROR&lt;/b&gt;: 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 "&lt;b&gt;ERROR&lt;/b&gt;: 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, "&lt;$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 &lt; $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##

大型虛擬主機(jī)

說(shuō)明:

Apache的<VirtualHost>功能很強(qiáng),在有幾十個(gè)虛擬主機(jī)的情況下運(yùn)行得很好,但是如果你是ISP,需要提供幾百個(gè)虛擬主機(jī),那么這就不是一個(gè)最佳的選擇了。

方案:

為此,需要用代理吞吐(Proxy Throughput)功能(flag [P])映射遠(yuǎn)程頁(yè)面甚至整個(gè)遠(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}}]
    :
top

對(duì)訪問(wèn)的限制

阻止Robots

說(shuō)明:

如何阻止一個(gè)完全匿名的robot取得特定網(wǎng)絡(luò)區(qū)域的頁(yè)面?一個(gè)/robots.txt文件可以包含若干"robot排除協(xié)議"的行,但不足以阻止此類robot。

方案:

可以用一個(gè)規(guī)則集以拒絕對(duì)網(wǎng)絡(luò)區(qū)域/~quux/foo/arc/(對(duì)一個(gè)很深的目錄區(qū)域進(jìn)行列表可能會(huì)使服務(wù)器產(chǎn)生很大的負(fù)載)的訪問(wèn)。還必須確保僅阻止特定的robot,就是說(shuō),僅僅阻止robot訪問(wèn)主機(jī)是不夠的,這樣會(huì)同時(shí)也阻止了用戶訪問(wèn)該主機(jī)。為此,就需要對(duì)HTTP頭的User-Agent信息作匹配。

RewriteCond %{HTTP_USER_AGENT}   ^NameOfBadRobot.*
RewriteCond %{REMOTE_ADDR}       ^123\.45\.67\.[8-9]$
RewriteRule ^/~quux/foo/arc/.+   -   [F]

阻止內(nèi)嵌的圖片

說(shuō)明:

假設(shè),http://www.quux-corp.de/~quux/有一些內(nèi)嵌圖片的頁(yè)面,這些圖片很好,所以就有人用超鏈連到他們自己的頁(yè)面中了。由于這樣徒然增加了我們的服務(wù)器的流量,因此,我們不愿意這種事情發(fā)生。

方案:

雖然,我們不能100%地保護(hù)這些圖片不被寫(xiě)入別人的頁(yè)面,但至少可以對(duì)發(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]

對(duì)主機(jī)的拒絕

說(shuō)明:

如何拒絕一批外部列表中的主機(jī)對(duì)我們服務(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]

對(duì)代理的拒絕

說(shuō)明:

如何拒絕某個(gè)主機(jī)或者來(lái)自特定主機(jī)的用戶使用Apache代理?

方案:

首先,要確保Apache web服務(wù)器在編譯時(shí)配置文件中mod_rewritemod_proxy的下面!使它在mod_proxy之前被調(diào)用。然后,如下拒絕某個(gè)主機(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)證

說(shuō)明:

有時(shí)候,會(huì)需要一種非常特殊的認(rèn)證,即對(duì)一組明確指定的用戶,允許其訪問(wèn),而沒(méi)有(在使用mod_access的基本認(rèn)證方法時(shí)可能會(huì)出現(xiàn)的)任何提示。

方案:

可是使用一個(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]

基于提交者(Referer)的反射器

說(shuō)明:

如何配置一個(gè)基于HTTP頭"Referer"的反射器以反射到任意數(shù)量的提交頁(yè)面?

方案:

使用這個(gè)很巧妙的規(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é)合對(duì)應(yīng)的重寫(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)將請(qǐng)求(在映射地圖中指定了"-"值的時(shí)候)反射回其提交頁(yè)面,或者(在映射地圖中URL有第二個(gè)參數(shù)時(shí))反射到一個(gè)特定的URL。

top

其他

外部重寫(xiě)引擎

說(shuō)明:

一個(gè)常見(jiàn)的問(wèn)題是如何解決似乎無(wú)法用mod_rewrite解決的FOO/BAR/QUUX/之類的問(wèn)題?

方案:

可以使用一個(gè)與RewriteMap功能相同的外部RewriteMap程序,一旦它在Apache啟動(dòng)時(shí)被執(zhí)行,則從STDIN接收被請(qǐng)求的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í)上,可以把它修改以獲得任何你需要的功能。但是要注意,雖然一般用戶都可以使用,可是只有系統(tǒng)管理員才可以定義這樣的地圖。