令狐冲
2008-11-5 18:31:50
Apache的mod_rewrite是提供了强大URL操作的杀手级的模块,可以实现几乎所有你梦想的URL操作类型,其代价是你必须接受其复杂性,因为mod_rewrite的主要障碍就是初学者不容易理解和运用,即使是Apache专家有时也会发掘出mod_rewrite的新用途。
换句话说:对mod_rewrite,或者是打退堂鼓永不再用,或者是喜欢它并一生受用。 ReWrite可以应用在以下方面或者解决以下问题: URL的规划 规范的URL 说明: 在有些网站服务器上,一个资源会拥有多个URL,在实际应用和发布中应该被使用的是规范的URL,其他的则是简写或者是内部使用的。无论用户在请求中使用什么形式的URL,他最终看见的都应该是规范的URL。 方案: 对所有的不规范的URL执行一个外部的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 说明: 通常,网站服务器的DocumentRoot直接对应于URL"/",但是,它常常不是处于最高一级,而可能只是众多数据池中的一个实体。比如,在Intranet站点中,有/e/www/(WWW的主页)、/e/sww/ (Intranet的主页)等等,而DocumentRoot指向了/e/www/,则必须保证此数据池中的所有内嵌的图片和其他元素对后继请求有效。 方案: 只须重定向URL /到/e/www/即可。这个方案看起来很简单,但只是有了mod_rewrite模块的支持,它才简单,因为传统的URL Aliases机制(由mod_alias及其相关模块提供)只是作了一个前缀匹配,DocumentRoot是一个对所有URL的前缀,因而无法实现这样的重定向。而用mod_rewrite的确很简单: 代码: RewriteEngine on RewriteRule ^/$ /e/www/ [R] 后缀斜杠的问题 说明: 每个网管对引用目录后缀斜杠的问题都有一本苦经,如果遗漏了,服务器会产生一个错误,因为如果请求是/~quux/foo而不是/~quux/foo/,服务器会去找一个叫foo的文件,而它是一个目录,所以就报错了。事实上,大多数情况下,它自己会试图修正这个错误,但是有时候需要你手工纠正,比如,在重写了许多CGI脚本中的复杂的URL以后。 方案: 解决这个微妙问题的方案是让服务器自动添加后缀的斜杠。对此,必须使用一个外部的重定向,使浏览器正确地处理后继的对诸如图片的请求。如果仅仅作一个内部的重写,可能只对目录页面有效,而对内嵌有使用相对URL的图片的页面则无效,因为浏览器有请求内嵌目标的可能。比如,如果不用外部重定向,/~quux/foo/index.html页面中对image.gif的请求,其结果将是/~quux/image.gif!。 所以,应该这样写: 代码: RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo$ foo/ [R] 又懒又疯狂的做法是把这些写入其宿主目录中的顶级.htaccess中,但是须注意,如此会带来一些处理上的开销。 代码: RewriteEngine on RewriteBase /~quux/ RewriteCond %{REQUEST_FILENAME} -d RewriteRule ^(.+[^/])$ $1/ [R] 集群网站的同类URL规划 说明: 我们希望在一个Intranet集群网站中,对所有WWW服务器建立一个同类的一致性的URL规划,也就是,所有的URL(对单个服务器来说,是本地的依赖于此服务器的!)是独立于服务器的!我们需要的是一个具有独立于服务器的一致性规划的WWW名称空间,即,URL不需要包含正确的物理的目标服务器,而由集群本身来自动定位物理的目标主机。 方案: 首先,目标服务器的信息来自(产生)于包含有用户、组以及实体的外部地图,其格式形如: 代码: user1 server_of_user1 user2 server_of_user2 : : 这些信息被存入map.xxx-to-host文件。其次,如果URL在一个服务器上无效,需要引导所有的服务器重定向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 以下规则集依靠地图文件来完成这个操作(假定,如果一个用户在地图中没有对应的项,则使用server0为默认服务器): 代码: 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\ 移动宿主目录到不同的网站服务器 说明: 通常,许多网管在建立一个新的网站服务器时,都会有这样的要求:重定向一个网站服务器上的所有宿主目录到另一个网站服务器。 方案: 很简单,用mod_rewrite。在老的网站服务器上重定向所有的URL /~user/anypath到http://newserver/~user/anypath。 |
令狐冲
2008-11-5 18:32:21
代码:
RewriteEngine on RewriteRule ^/~(.+) http://newserver/~$1 [R,L] 结构化的宿主目录 说明: 一些拥有几千个用户的网站通常都使用结构化的宿主目录规划,即,每个宿主目录位于一个带有特定前缀比如其用户名的第一个字符的子目录下。那么,/~foo/anypath代表/home/f/foo/.www/anypath,而/~bar/anypath代表/home/b/bar/.www/anypath。 方案: 可以使用下列规则集来扩展~以达到上述目的。 代码: RewriteEngine on RewriteRule ^/~(([a-z])[a-z0-9]+)(.*) /home/$2/$1/.www$3 文件系统的重组 说明: 这是一个不加雕琢的例子:一个大量使用针对目录的规则集以实现平滑观感,而从来不用调整数据结构的杀手级的应用。背景:net.sw从1992年开始,存放了我收集的免费的有效的Unix软件包。它是我的爱好也是我的工作,因为在学习计算机科学的同时,业余时间还做了多年的系统和网络的管理员。每周我都需要整理软件,因而建立了一个层次很深的目录结构来存放各种软件包: 代码: 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接口公开我的收藏。“漂亮”是指提供一个接口以直接浏览整个目录结构,同时不对这个结构做任何改变 - 甚至也不在结构顶部放置CGI脚本。为什么呢?因为这个结构还要能够被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/子目录包含了上述目录结构,即实在的net.sw,由rdist在需要的时候自动更新。第二个部分的遗留问题是:如何连接这两个结构为一个平滑观感的URL树?我希望在运行适当的CGI脚本而使用各种URL的时候,使用户感觉不到DATA/目录的存在。方案如下:首先,我把下列配置放在服务器上DocumentRoot中的针对目录的配置文件里,以重写公布的URL /net.sw/ 为内部路径 /e/netsw: 代码: RewriteRule ^net.sw$ net.sw/ [R] RewriteRule ^net.sw/(.*)$ e/netsw/$1 第一条规则是针对遗漏后缀斜杠的请求的!第二条规则才是真正实现功能的。接着,就是放在针对目录的配置文件/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(最后),和无对应项('-') 注意后半部分中的符号!(非),和标志C (链) 注意最后一条规则的全匹配模式 代码: NCSA imagemap和Apache mod_imap 说明: 许多人都希望在从NCSA网站服务器向较现代的Apache网站服务器转移中实现平滑过渡,即希望老的NCSA imagemap程序能在Apache的较现代的mod_imap支持下正常运作。但问题在于,到处都是通过/cgi-bin/imagemap/path/to/page.map引用imagemap程序的连接,而在Apache下,应该写成/path/to/page.map。 方案: 使用全局规则在空闲时间去除所有这些请求的前缀: 代码: RewriteEngine on RewriteRule ^/cgi-bin/imagemap(.*) $1 [PT] 在多个目录中搜索页面 说明: 有时会有必要使网站服务器在多个目录中搜索页面,对此,MultiViews或者其他技术无能为力。 方案: 编制一个明确的规则集以搜索目录中的文件。 代码: 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] 按照URL的片段设置环境变量 说明: 如果希望保持请求之间的状态信息,但又不希望使用CGI来包装所有页面,而只通过分离URL中的有用信息来编码。 方案: 可以用一个规则集来分离出状态信息,并设置环境变量以备此后用于XSSI或CGI。如此,一个/foo/S=java/bar/的URL会被解析为/foo/bar/,而环境变量STATUS则被设置为"java"。 代码: RewriteEngine on RewriteRule ^(.*)/S=([^/]+)/(.*) $1/$3 [E=STATUS:$2] 虚拟用户主机 说明: 如果需要为用户username支持一个www.username.host.domain.com的主页,但不是用在此机器上建虚拟主机的方法,而是用仅在此机器上增加一个DNS记录的方法实现。 方案: 对HTTP/1.0的请求,这是无法实现的;但是对HTTP/1.1的在HTTP头中包含有主机名的请求,可以用以下规则集来内部地重写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到另一个网站服务器www.somewhere.com,有时这种做法也会用在虚拟主机的上下文中。 方案: 只须一个重写条件: 代码: RewriteEngine on RewriteCond %{REMOTE_HOST} !^.+\.ourdomain\.com$ RewriteRule ^(/~.+) http://www.somewhere.com/$1 [R,L] 重定向失败的URL到其他网站服务器 说明: 如何重写URL以重定向对网站服务器A的失败请求到服务器B,是一个常见的问题。一般,可以用Perl写的CGI脚本通过ErrorDocument来解决,此外,还有mod_rewrite方案。但是须注意,这种方法的执行效率不如用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类型都有效而且安全的方法。但是,对网站服务器的性能会有影响,所以如果网站服务器有一个强大的CPU,那就用这个方法。而在慢速机器上,可以用第一种方法,或者用性能更好的ErrorDocument CGI脚本。 扩展的重定向 说明: 有时候,我们会需要更多的对重定向URL的(有关字符转义机制方面的)控制。通常,Apache内核中的URL转义函数uri_escape()同时还会对anchor转义,即,类似"url#anchor"的URL,因此,你不能用mod_rewrite对此类URL直接重定向。那么如何实现呢? 方案: 必须用NPH-CGI脚本使它自己重定向,因为对NPH(non-parseable headers [无须解析的HTTP头])不会发生转义操作。首先,在针对服务器的配置中(应该位于所有重写规则的最后),引入一种新的URL类型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类型的方法,包括不被mod_rewrite直接支持的类型。所以,还可以这样重定向news:newsgroup: 代码: RewriteRule ^anyurl xredirect:news:newsgroup 注意:无须对上述规则加[R]或[R,L],因为xredirect:会在稍后被其特殊的传送规则扩展。 文档访问的多路复用 说明: 你知道http://www.perl.com/CPAN的CPAN(Comprehensive Perl Archive Network)吗?它实现了一个重定向以提供,全世界的CPAN镜像中离访问者最近的一个FTP站点,也可以称之为FTP访问多路复用服务。CPAN是通过CGI脚本实现的,那么用mod_rewrite如何实现呢? 方案: 首先,我们注意到mod_rewrite从3.0.0版本开始,还可以重写"ftp:"类型。其次,对客户端顶级域名的路径最近的求取可以用RewriteMap实现。利用链式规则集,并用顶级域名作为查找多路复用地图的键,可以这样做: 代码: 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## 依赖于时间的重写 说明: 在页面内容依时间不同而变化的场合,比如重定向特定页面,许多网管仍然采用CGI脚本的方法,如何用mod_rewrite来实现呢? 方案: 有许多类似TIME_xxx的变量可以用在重写条件中,利用<STRING,>STRING和=STRING的类型比较,并加以连接,就可以实现依赖于时间的重写: 代码: 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时指向foo.day.html,而在其余时间,则指向foo.night.html,对主页是一个不错的功能... 对YYYY过渡为XXXX的向前兼容 说明: 在转变了大批.html文件为.phtml,使文档.YYYY过渡成为文档.XXXX后,如何保持URL的向前兼容(仍然虚拟地存在)? 方案: 只须按基准文件名重写,并测试带有新的扩展名的文件是否存在,如果存在,则用新的,否则,仍然用原来的。 代码: # 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 内容的处理 新旧URL(内部的) 说明: 假定已经把文件bar.html改名为foo.html,需要对老的URL向前兼容,即让用户仍然可以使用老的URL,而感觉不到文件被改名了。 方案: 通过以下规则内部地重写老的URL为新的: 代码: RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo\.html$ bar.html 新旧URL(外部的) 说明: 仍然假定已经把文件bar.html改名为foo.html,需要对老的URL向前兼容,但是要让用户得到文件被改名的暗示,即,其浏览器的地址栏中显示的是新的URL。 方案: 作一个HTTP的强制重定向以改变浏览器和用户界面上的显示: 代码: RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo\.html$ bar.html [R] 依赖于浏览器的内容 说明: 至少对重要的顶级页面,有时候有必要提供依赖于浏览器的最佳的内容,即对最新的Netscape提供最大化的版本,对Lynx提供最小化的版本,而对其他的浏览器则提供一个功能一般的版本。 方案: 对此,内容协商无能为力,因为浏览器不提供其那种形式的类型,所以只能在HTTP头"User-Agent"上想办法。以下规则集可以完成这个操作:如果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] 动态镜像 说明: 假定,需要在我们的名称空间里加入其他远程主机的页面。对FTP服务器,可以用mirror程序以在本地机器上维持一个对远程数据的最新的拷贝;对网站服务器,可以用类似的用于HTTP的webcopy程序。但这两种技术都有一个主要的缺点:此本地拷贝必须通过这个程序的执行来更新。所以,比较好的方法是,不采用静态镜像,而采用动态镜像,即,在有数据请求时自动更新(远程主机上更新的数据)。 方案: 为此,使用Proxy Throughput功能(flag [P]),以映射远程页面甚至整个远程网络区域到我们的名称空间: 代码: 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 |
令狐冲
2008-11-5 18:32:29
代码:
RewriteEngine on RewriteRule ^/~(.+) http://newserver/~$1 [R,L] 结构化的宿主目录 说明: 一些拥有几千个用户的网站通常都使用结构化的宿主目录规划,即,每个宿主目录位于一个带有特定前缀比如其用户名的第一个字符的子目录下。那么,/~foo/anypath代表/home/f/foo/.www/anypath,而/~bar/anypath代表/home/b/bar/.www/anypath。 方案: 可以使用下列规则集来扩展~以达到上述目的。 代码: RewriteEngine on RewriteRule ^/~(([a-z])[a-z0-9]+)(.*) /home/$2/$1/.www$3 文件系统的重组 说明: 这是一个不加雕琢的例子:一个大量使用针对目录的规则集以实现平滑观感,而从来不用调整数据结构的杀手级的应用。背景:net.sw从1992年开始,存放了我收集的免费的有效的Unix软件包。它是我的爱好也是我的工作,因为在学习计算机科学的同时,业余时间还做了多年的系统和网络的管理员。每周我都需要整理软件,因而建立了一个层次很深的目录结构来存放各种软件包: 代码: 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接口公开我的收藏。“漂亮”是指提供一个接口以直接浏览整个目录结构,同时不对这个结构做任何改变 - 甚至也不在结构顶部放置CGI脚本。为什么呢?因为这个结构还要能够被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/子目录包含了上述目录结构,即实在的net.sw,由rdist在需要的时候自动更新。第二个部分的遗留问题是:如何连接这两个结构为一个平滑观感的URL树?我希望在运行适当的CGI脚本而使用各种URL的时候,使用户感觉不到DATA/目录的存在。方案如下:首先,我把下列配置放在服务器上DocumentRoot中的针对目录的配置文件里,以重写公布的URL /net.sw/ 为内部路径 /e/netsw: 代码: RewriteRule ^net.sw$ net.sw/ [R] RewriteRule ^net.sw/(.*)$ e/netsw/$1 第一条规则是针对遗漏后缀斜杠的请求的!第二条规则才是真正实现功能的。接着,就是放在针对目录的配置文件/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(最后),和无对应项('-') 注意后半部分中的符号!(非),和标志C (链) 注意最后一条规则的全匹配模式 代码: NCSA imagemap和Apache mod_imap 说明: 许多人都希望在从NCSA网站服务器向较现代的Apache网站服务器转移中实现平滑过渡,即希望老的NCSA imagemap程序能在Apache的较现代的mod_imap支持下正常运作。但问题在于,到处都是通过/cgi-bin/imagemap/path/to/page.map引用imagemap程序的连接,而在Apache下,应该写成/path/to/page.map。 方案: 使用全局规则在空闲时间去除所有这些请求的前缀: 代码: RewriteEngine on RewriteRule ^/cgi-bin/imagemap(.*) $1 [PT] 在多个目录中搜索页面 说明: 有时会有必要使网站服务器在多个目录中搜索页面,对此,MultiViews或者其他技术无能为力。 方案: 编制一个明确的规则集以搜索目录中的文件。 代码: 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] 按照URL的片段设置环境变量 说明: 如果希望保持请求之间的状态信息,但又不希望使用CGI来包装所有页面,而只通过分离URL中的有用信息来编码。 方案: 可以用一个规则集来分离出状态信息,并设置环境变量以备此后用于XSSI或CGI。如此,一个/foo/S=java/bar/的URL会被解析为/foo/bar/,而环境变量STATUS则被设置为"java"。 代码: RewriteEngine on RewriteRule ^(.*)/S=([^/]+)/(.*) $1/$3 [E=STATUS:$2] 虚拟用户主机 说明: 如果需要为用户username支持一个www.username.host.domain.com的主页,但不是用在此机器上建虚拟主机的方法,而是用仅在此机器上增加一个DNS记录的方法实现。 方案: 对HTTP/1.0的请求,这是无法实现的;但是对HTTP/1.1的在HTTP头中包含有主机名的请求,可以用以下规则集来内部地重写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到另一个网站服务器www.somewhere.com,有时这种做法也会用在虚拟主机的上下文中。 方案: 只须一个重写条件: 代码: RewriteEngine on RewriteCond %{REMOTE_HOST} !^.+\.ourdomain\.com$ RewriteRule ^(/~.+) http://www.somewhere.com/$1 [R,L] 重定向失败的URL到其他网站服务器 说明: 如何重写URL以重定向对网站服务器A的失败请求到服务器B,是一个常见的问题。一般,可以用Perl写的CGI脚本通过ErrorDocument来解决,此外,还有mod_rewrite方案。但是须注意,这种方法的执行效率不如用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类型都有效而且安全的方法。但是,对网站服务器的性能会有影响,所以如果网站服务器有一个强大的CPU,那就用这个方法。而在慢速机器上,可以用第一种方法,或者用性能更好的ErrorDocument CGI脚本。 扩展的重定向 说明: 有时候,我们会需要更多的对重定向URL的(有关字符转义机制方面的)控制。通常,Apache内核中的URL转义函数uri_escape()同时还会对anchor转义,即,类似"url#anchor"的URL,因此,你不能用mod_rewrite对此类URL直接重定向。那么如何实现呢? 方案: 必须用NPH-CGI脚本使它自己重定向,因为对NPH(non-parseable headers [无须解析的HTTP头])不会发生转义操作。首先,在针对服务器的配置中(应该位于所有重写规则的最后),引入一种新的URL类型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类型的方法,包括不被mod_rewrite直接支持的类型。所以,还可以这样重定向news:newsgroup: 代码: RewriteRule ^anyurl xredirect:news:newsgroup 注意:无须对上述规则加[R]或[R,L],因为xredirect:会在稍后被其特殊的传送规则扩展。 文档访问的多路复用 说明: 你知道http://www.perl.com/CPAN的CPAN(Comprehensive Perl Archive Network)吗?它实现了一个重定向以提供,全世界的CPAN镜像中离访问者最近的一个FTP站点,也可以称之为FTP访问多路复用服务。CPAN是通过CGI脚本实现的,那么用mod_rewrite如何实现呢? 方案: 首先,我们注意到mod_rewrite从3.0.0版本开始,还可以重写"ftp:"类型。其次,对客户端顶级域名的路径最近的求取可以用RewriteMap实现。利用链式规则集,并用顶级域名作为查找多路复用地图的键,可以这样做: 代码: 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## 依赖于时间的重写 说明: 在页面内容依时间不同而变化的场合,比如重定向特定页面,许多网管仍然采用CGI脚本的方法,如何用mod_rewrite来实现呢? 方案: 有许多类似TIME_xxx的变量可以用在重写条件中,利用<STRING,>STRING和=STRING的类型比较,并加以连接,就可以实现依赖于时间的重写: 代码: 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时指向foo.day.html,而在其余时间,则指向foo.night.html,对主页是一个不错的功能... 对YYYY过渡为XXXX的向前兼容 说明: 在转变了大批.html文件为.phtml,使文档.YYYY过渡成为文档.XXXX后,如何保持URL的向前兼容(仍然虚拟地存在)? 方案: 只须按基准文件名重写,并测试带有新的扩展名的文件是否存在,如果存在,则用新的,否则,仍然用原来的。 代码: # 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 内容的处理 新旧URL(内部的) 说明: 假定已经把文件bar.html改名为foo.html,需要对老的URL向前兼容,即让用户仍然可以使用老的URL,而感觉不到文件被改名了。 方案: 通过以下规则内部地重写老的URL为新的: 代码: RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo\.html$ bar.html 新旧URL(外部的) 说明: 仍然假定已经把文件bar.html改名为foo.html,需要对老的URL向前兼容,但是要让用户得到文件被改名的暗示,即,其浏览器的地址栏中显示的是新的URL。 方案: 作一个HTTP的强制重定向以改变浏览器和用户界面上的显示: 代码: RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo\.html$ bar.html [R] 依赖于浏览器的内容 说明: 至少对重要的顶级页面,有时候有必要提供依赖于浏览器的最佳的内容,即对最新的Netscape提供最大化的版本,对Lynx提供最小化的版本,而对其他的浏览器则提供一个功能一般的版本。 方案: 对此,内容协商无能为力,因为浏览器不提供其那种形式的类型,所以只能在HTTP头"User-Agent"上想办法。以下规则集可以完成这个操作:如果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] 动态镜像 说明: 假定,需要在我们的名称空间里加入其他远程主机的页面。对FTP服务器,可以用mirror程序以在本地机器上维持一个对远程数据的最新的拷贝;对网站服务器,可以用类似的用于HTTP的webcopy程序。但这两种技术都有一个主要的缺点:此本地拷贝必须通过这个程序的执行来更新。所以,比较好的方法是,不采用静态镜像,而采用动态镜像,即,在有数据请求时自动更新(远程主机上更新的数据)。 方案: 为此,使用Proxy Throughput功能(flag [P]),以映射远程页面甚至整个远程网络区域到我们的名称空间: 代码: 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 |
令狐冲
2008-11-5 18:32:36
代码:
RewriteEngine on RewriteRule ^/~(.+) http://newserver/~$1 [R,L] 结构化的宿主目录 说明: 一些拥有几千个用户的网站通常都使用结构化的宿主目录规划,即,每个宿主目录位于一个带有特定前缀比如其用户名的第一个字符的子目录下。那么,/~foo/anypath代表/home/f/foo/.www/anypath,而/~bar/anypath代表/home/b/bar/.www/anypath。 方案: 可以使用下列规则集来扩展~以达到上述目的。 代码: RewriteEngine on RewriteRule ^/~(([a-z])[a-z0-9]+)(.*) /home/$2/$1/.www$3 文件系统的重组 说明: 这是一个不加雕琢的例子:一个大量使用针对目录的规则集以实现平滑观感,而从来不用调整数据结构的杀手级的应用。背景:net.sw从1992年开始,存放了我收集的免费的有效的Unix软件包。它是我的爱好也是我的工作,因为在学习计算机科学的同时,业余时间还做了多年的系统和网络的管理员。每周我都需要整理软件,因而建立了一个层次很深的目录结构来存放各种软件包: 代码: 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接口公开我的收藏。“漂亮”是指提供一个接口以直接浏览整个目录结构,同时不对这个结构做任何改变 - 甚至也不在结构顶部放置CGI脚本。为什么呢?因为这个结构还要能够被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/子目录包含了上述目录结构,即实在的net.sw,由rdist在需要的时候自动更新。第二个部分的遗留问题是:如何连接这两个结构为一个平滑观感的URL树?我希望在运行适当的CGI脚本而使用各种URL的时候,使用户感觉不到DATA/目录的存在。方案如下:首先,我把下列配置放在服务器上DocumentRoot中的针对目录的配置文件里,以重写公布的URL /net.sw/ 为内部路径 /e/netsw: 代码: RewriteRule ^net.sw$ net.sw/ [R] RewriteRule ^net.sw/(.*)$ e/netsw/$1 第一条规则是针对遗漏后缀斜杠的请求的!第二条规则才是真正实现功能的。接着,就是放在针对目录的配置文件/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(最后),和无对应项('-') 注意后半部分中的符号!(非),和标志C (链) 注意最后一条规则的全匹配模式 代码: NCSA imagemap和Apache mod_imap 说明: 许多人都希望在从NCSA网站服务器向较现代的Apache网站服务器转移中实现平滑过渡,即希望老的NCSA imagemap程序能在Apache的较现代的mod_imap支持下正常运作。但问题在于,到处都是通过/cgi-bin/imagemap/path/to/page.map引用imagemap程序的连接,而在Apache下,应该写成/path/to/page.map。 方案: 使用全局规则在空闲时间去除所有这些请求的前缀: 代码: RewriteEngine on RewriteRule ^/cgi-bin/imagemap(.*) $1 [PT] 在多个目录中搜索页面 说明: 有时会有必要使网站服务器在多个目录中搜索页面,对此,MultiViews或者其他技术无能为力。 方案: 编制一个明确的规则集以搜索目录中的文件。 代码: 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] 按照URL的片段设置环境变量 说明: 如果希望保持请求之间的状态信息,但又不希望使用CGI来包装所有页面,而只通过分离URL中的有用信息来编码。 方案: 可以用一个规则集来分离出状态信息,并设置环境变量以备此后用于XSSI或CGI。如此,一个/foo/S=java/bar/的URL会被解析为/foo/bar/,而环境变量STATUS则被设置为"java"。 代码: RewriteEngine on RewriteRule ^(.*)/S=([^/]+)/(.*) $1/$3 [E=STATUS:$2] 虚拟用户主机 说明: 如果需要为用户username支持一个www.username.host.domain.com的主页,但不是用在此机器上建虚拟主机的方法,而是用仅在此机器上增加一个DNS记录的方法实现。 方案: 对HTTP/1.0的请求,这是无法实现的;但是对HTTP/1.1的在HTTP头中包含有主机名的请求,可以用以下规则集来内部地重写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到另一个网站服务器www.somewhere.com,有时这种做法也会用在虚拟主机的上下文中。 方案: 只须一个重写条件: 代码: RewriteEngine on RewriteCond %{REMOTE_HOST} !^.+\.ourdomain\.com$ RewriteRule ^(/~.+) http://www.somewhere.com/$1 [R,L] 重定向失败的URL到其他网站服务器 说明: 如何重写URL以重定向对网站服务器A的失败请求到服务器B,是一个常见的问题。一般,可以用Perl写的CGI脚本通过ErrorDocument来解决,此外,还有mod_rewrite方案。但是须注意,这种方法的执行效率不如用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类型都有效而且安全的方法。但是,对网站服务器的性能会有影响,所以如果网站服务器有一个强大的CPU,那就用这个方法。而在慢速机器上,可以用第一种方法,或者用性能更好的ErrorDocument CGI脚本。 扩展的重定向 说明: 有时候,我们会需要更多的对重定向URL的(有关字符转义机制方面的)控制。通常,Apache内核中的URL转义函数uri_escape()同时还会对anchor转义,即,类似"url#anchor"的URL,因此,你不能用mod_rewrite对此类URL直接重定向。那么如何实现呢? 方案: 必须用NPH-CGI脚本使它自己重定向,因为对NPH(non-parseable headers [无须解析的HTTP头])不会发生转义操作。首先,在针对服务器的配置中(应该位于所有重写规则的最后),引入一种新的URL类型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类型的方法,包括不被mod_rewrite直接支持的类型。所以,还可以这样重定向news:newsgroup: 代码: RewriteRule ^anyurl xredirect:news:newsgroup 注意:无须对上述规则加[R]或[R,L],因为xredirect:会在稍后被其特殊的传送规则扩展。 文档访问的多路复用 说明: 你知道http://www.perl.com/CPAN的CPAN(Comprehensive Perl Archive Network)吗?它实现了一个重定向以提供,全世界的CPAN镜像中离访问者最近的一个FTP站点,也可以称之为FTP访问多路复用服务。CPAN是通过CGI脚本实现的,那么用mod_rewrite如何实现呢? 方案: 首先,我们注意到mod_rewrite从3.0.0版本开始,还可以重写"ftp:"类型。其次,对客户端顶级域名的路径最近的求取可以用RewriteMap实现。利用链式规则集,并用顶级域名作为查找多路复用地图的键,可以这样做: 代码: 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## 依赖于时间的重写 说明: 在页面内容依时间不同而变化的场合,比如重定向特定页面,许多网管仍然采用CGI脚本的方法,如何用mod_rewrite来实现呢? 方案: 有许多类似TIME_xxx的变量可以用在重写条件中,利用<STRING,>STRING和=STRING的类型比较,并加以连接,就可以实现依赖于时间的重写: 代码: 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时指向foo.day.html,而在其余时间,则指向foo.night.html,对主页是一个不错的功能... 对YYYY过渡为XXXX的向前兼容 说明: 在转变了大批.html文件为.phtml,使文档.YYYY过渡成为文档.XXXX后,如何保持URL的向前兼容(仍然虚拟地存在)? 方案: 只须按基准文件名重写,并测试带有新的扩展名的文件是否存在,如果存在,则用新的,否则,仍然用原来的。 代码: # 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 内容的处理 新旧URL(内部的) 说明: 假定已经把文件bar.html改名为foo.html,需要对老的URL向前兼容,即让用户仍然可以使用老的URL,而感觉不到文件被改名了。 方案: 通过以下规则内部地重写老的URL为新的: 代码: RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo\.html$ bar.html 新旧URL(外部的) 说明: 仍然假定已经把文件bar.html改名为foo.html,需要对老的URL向前兼容,但是要让用户得到文件被改名的暗示,即,其浏览器的地址栏中显示的是新的URL。 方案: 作一个HTTP的强制重定向以改变浏览器和用户界面上的显示: 代码: RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo\.html$ bar.html [R] 依赖于浏览器的内容 说明: 至少对重要的顶级页面,有时候有必要提供依赖于浏览器的最佳的内容,即对最新的Netscape提供最大化的版本,对Lynx提供最小化的版本,而对其他的浏览器则提供一个功能一般的版本。 方案: 对此,内容协商无能为力,因为浏览器不提供其那种形式的类型,所以只能在HTTP头"User-Agent"上想办法。以下规则集可以完成这个操作:如果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] 动态镜像 说明: 假定,需要在我们的名称空间里加入其他远程主机的页面。对FTP服务器,可以用mirror程序以在本地机器上维持一个对远程数据的最新的拷贝;对网站服务器,可以用类似的用于HTTP的webcopy程序。但这两种技术都有一个主要的缺点:此本地拷贝必须通过这个程序的执行来更新。所以,比较好的方法是,不采用静态镜像,而采用动态镜像,即,在有数据请求时自动更新(远程主机上更新的数据)。 方案: 为此,使用Proxy Throughput功能(flag [P]),以映射远程页面甚至整个远程网络区域到我们的名称空间: 代码: 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 |
GMT+8, 2024-12-22 23:40