URL重寫指南
Originally written by
Ralf S. Engelschall <rse@apache.org>
December 1997
本文是mod_rewrite的參考文檔,闡述在實際應(yīng)用中如何解決網(wǎng)管所面臨的基于URL的典型問題,并詳細(xì)描述如何配置URL重寫規(guī)則集以解決問題。
(1)URL的規(guī)劃
規(guī)范的URL
說明:
在有些網(wǎng)站服務(wù)器上,一個資源會擁有多個URL,在實際應(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]
(2)規(guī)范的主機名
方案:
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]
(3)被移動過的DocumentRoot
說明:
通常,網(wǎng)站服務(wù)器的DocumentRoot直接對應(yīng)于URL"/",但是,它常常不是處于最高一級,而可能只是眾多數(shù)據(jù)池中的一個實體。比如,在Intranet站點中,有/e/www/(WWW的主頁)、/e/sww/ (Intranet的主頁)等等,而DocumentRoot指向了/e/www/,則必須保證此數(shù)據(jù)池中的所有內(nèi)嵌的圖片和其他元素對后繼請求有效。
方案:
只須重定向URL /到/e/www/即可。這個方案看起來很簡單,但只是有了mod_rewrite模塊的支持,它才簡單,因為傳統(tǒng)的URL Aliases機制(由mod_alias及其相關(guān)模塊提供)只是作了一個前綴匹配,DocumentRoot是一個對所有URL的前綴,因而無法實現(xiàn)這樣的重定向。而用mod_rewrite的確很簡單:
RewriteEngine on
RewriteRule ^/$ /e/www/ [R]
(4)后綴斜杠的問題
說明:
每個網(wǎng)管對引用目錄后綴斜杠的問題都有一本苦經(jīng),如果遺漏了,服務(wù)器會產(chǎn)生一個錯誤,因為如果請求是/~quux/foo而不是/~quux/foo/,服務(wù)器會去找一個叫foo的文件,而它是一個目錄,所以就報錯了。事實上,大多數(shù)情況下,它自己會試圖修正這個錯誤,但是有時候需要你手工糾正,比如,在重寫了許多CGI腳本中的復(fù)雜的URL以后。
方案:
解決這個微妙問題的方案是讓服務(wù)器自動添加后綴的斜杠。對此,必須使用一個外部的重定向,使瀏覽器正確地處理后繼的對諸如圖片的請求。如果僅僅作一個內(nèi)部的重寫,可能只對目錄頁面有效,而對內(nèi)嵌有使用相對URL的圖片的頁面則無效,因為瀏覽器有請求內(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]
(5)集群網(wǎng)站的同類URL規(guī)劃
到
http://physical-host/u/user/anypath
http://physical-host/g/group/anypath
http://physical-host/e/entity/anypath
以下規(guī)則集依靠地圖文件來完成這個操作(假定,如果一個用戶在地圖中沒有對應(yī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\
(6)移動宿主目錄到不同的網(wǎng)站服務(wù)器
說明:
通常,許多網(wǎng)管在建立一個新的網(wǎng)站服務(wù)器時,都會有這樣的要求:重定向一個網(wǎng)站服務(wù)器上的所有宿主目錄到另一個網(wǎng)站服務(wù)器。
方案:
很簡單,用mod_rewrite。在老的網(wǎng)站服務(wù)器上重定向所有的URL /~user/anypath到http://newserver/~user/anypath。
RewriteEngine on
RewriteRule ^/~(.+) http://newserver/~$1 [R,L]
(7)結(jié)構(gòu)化的宿主目錄
說明:
一些擁有幾千個用戶的網(wǎng)站通常都使用結(jié)構(gòu)化的宿主目錄規(guī)劃,即,每個宿主目錄位于一個帶有特定前綴比如其用戶名的第一個字符的子目錄下。那么,/~foo/anypath代表/home/f/foo/.www/anypath,而/~bar/anypath代表/home/b/bar/.www/anypath。
方案:
可以使用下列規(guī)則集來擴展~以達(dá)到上述目的。
RewriteEngine on
RewriteRule ^/~(([a-z])[a-z0-9]+)(.*) /home/$2/$1/.www$3
(8)文件系統(tǒng)的重組
說明:
這是一個不加雕琢的例子:一個大量使用針對目錄的規(guī)則集以實現(xiàn)平滑觀感,而從來不用調(diào)整數(shù)據(jù)結(jié)構(gòu)的殺手級的應(yīng)用。背景:net.sw從1992年開始,存放了我收集的免費的有效的Unix軟件包。它是我的愛好也是我的工作,因為在學(xué)習(xí)計算機科學(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/
(9)方案
這個方案分為兩個部分:第一個部分,是用于在空閑時間建立所有目錄頁面的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),即實在的net.sw,由rdist在需要的時候自動更新。第二個部分的遺留問題是:如何連接這兩個結(jié)構(gòu)為一個平滑觀感的URL樹?我希望在運行適當(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ī)則才是真正實現(xiàn)功能的。接著,就是放在針對目錄的配置文件/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
閱讀提示:
注意前半部分中的標(biāo)志L(最后),和無對應(yīng)項('-')
注意后半部分中的符號!(非),和標(biāo)志C (鏈)
注意最后一條規(guī)則的全匹配模式
(10)NCSA imagemap和Apache mod_imap
說明:
許多人都希望在從NCSA網(wǎng)站服務(wù)器向較現(xiàn)代的Apache網(wǎng)站服務(wù)器轉(zhuǎn)移中實現(xiàn)平滑過渡,即希望老的NCSA imagemap程序能在Apache的較現(xiàn)代的mod_imap支持下正常運作。但問題在于,到處都是通過/cgi-bin/imagemap/path/to/page.map引用imagemap程序的連接,而在Apache下,應(yīng)該寫成/path/to/page.map。
方案:
使用全局規(guī)則在空閑時間去除所有這些請求的前綴:
RewriteEngine on
RewriteRule ^/cgi-bin/imagemap(.*) $1 [PT]
更多數(shù)據(jù)請參考:Apache 2.0手冊中文版翻譯項目 http://man.chinaunix.net/newsoft/ApacheManual/misc/rewriteguide.html