高级URL重写指南

高级URL重写指南

本文是mod_rewrite参考文档的补充材料。阐述在实际应用中如何解决网管所面临的基于URL的典型问题,并详细描述了如何配置URL重写规则集以解决这些问题。

注意:根据你的服务器配置,有可能必须对这里的例子作些小修改,比如,在额外启用 mod_aliasmod_userdir的情况下要增加 [PT]标志,或者为了适应目录级( .htaccess)的配置而将针对服务器级的规则集进行重写。对一个特定的规则集应该先透彻理解然后再考虑应用,这样才能避免出现问题。
top

集群网站的同类URL规划

描述:
我们希望在一个Intranet集群网站中,对所有WWW服务器建立一致的URL规划,也就是说,所有的URL(针对每个服务器进行本地配置,因此是独立于各个服务器的)实际上都是 独立于各个服务器的!我们需要的是一个具有独立于各个服务器的一致性的WWW域名空间,即URL不需要包含物理目标服务器,而由集群本身来自动定位物理目标主机。
解决方案:
首先,目标服务器的信息来自于(分布式的)包含有用户、组以及实体的外部映射表,其格式形如下:
user1  server_of_user1
user2  server_of_user2
:      :
这些信息被存入 map.xxx-to-host文件。其次,如果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/
top

结构化的用户主目录

描述:
一些拥有几千个用户的网站通常都使用结构化的用户主目录规划,即每个用户主目录位于一个带有特定前缀(比如其用户名的第一个字符)的子目录下: /~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
top

文件系统的重组

描述:
这是一个不加雕琢的例子:一个大量使用目录级规则集以实现平滑的观感,并且从来不用调整数据结构的杀手级的应用。背景:从1992年开始, net.sw存放了我收集的免费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树?我希望在为各种URL运行对应的CGI脚本的时候,用户感觉不到 DATA/目录的存在。方案如下:首先,我把下列配置放在针对每个目录的配置文件里,将公布的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

# 通过"/net.sw/"前缀到达
RewriteBase   /net.sw/

# 首先将根目录重写到cgi处理脚本
RewriteRule   ^$                       netsw-home.cgi     [L]
RewriteRule   ^index/.html$            netsw-home.cgi     [L]

# 当浏览器请求perdir页面时剥去子目录
RewriteRule   ^.+/(netsw-[^/]+/.+)$    $1                 [L]

# 现在打断对本地文件的重写
RewriteRule   ^netsw-home/.cgi.*       -                  [L]
RewriteRule   ^netsw-changes/.cgi.*    -                  [L]
RewriteRule   ^netsw-search/.cgi.*     -                  [L]
RewriteRule   ^netsw-tree/.cgi$        -                  [L]
RewriteRule   ^netsw-about/.html$      -                  [L]
RewriteRule   ^netsw-img/.*$           -                  [L]

# 任何别的东西都是一个由另一个cgi脚本处理的子目录
RewriteRule   !^netsw-lsdir/.cgi.*     -                  [C]
RewriteRule   (.*)                     netsw-lsdir.cgi/$1

阅读提示:

  1. 注意前半部分中的L(最后)标志和非替换部分('-')
  2. 注意后半部分中的!(非)符号和C(链)标志
  3. 注意最后一条规则是全匹配模式
top

将失败的URL重定向到其他web服务器

描述:
一个常见的问题是如何将对web服务器A的失败请求重定向到服务器B。一般,可以使用借助 ErrorDocument的CGI脚本来解决,此外,还有使用 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类型都有效而且安全的方法,但是对web服务器的性能有不利影响。如果web服务器有一个强大的CPU,那就用这个方法。而在慢速机器上,可以用第一种方法,或者用性能更好的CGI脚本。
top

文档访问的多路复用

描述:
你知道 http://www.perl.com/CPAN的CPAN(综合Perl存档网络)吗?它实现了一个重定向以提供全世界的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 -- CxAN多路映射表
##

de        ftp://ftp.cxan.de/CxAN/
uk        ftp://ftp.cxan.uk/CxAN/
com       ftp://ftp.cxan.com/CxAN/
 :
##EOF##
top

内容处理

依赖于浏览器的内容

描述:
有时候有必须提供依赖于浏览器的最佳内容(至少对重要的顶级页面),即对最新的Netscape提供最大化的版本,对Lynx提供最小化的版本,而对其他的浏览器则提供一个一般的版本。
解决方案:
对此,内容协商无能为力,因为浏览器不提供那种形式的类型,所以只能在"User-Agent"头上想办法。以下规则集可以完成这个操作:如果"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)功能( [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

通过Intranet取得丢失的数据

描述:
这在受防火墙保护的内部网( www2.quux-corp.dom)上保存和维护实际数据,而在Internet上虚拟地运行web服务器( www.quux-corp.dom)。方法是外部服务器在空闲时间从内部服务器取得被请求的数据。
解决方案:
首先,必须确保防火墙对内部服务器的保护,并只允许此外部服务器获取数据。对包过滤(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
请按你的实际情况,对上例稍作调整。接着,建立通过代理后台获取丢失数据的重写规则:
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个服务器)?
解决方案:
这个问题有许多可能的解决方案,在此,我们讨论通称为"基于DNS"的方案,和特殊的使用 mod_rewrite的方案
  1. DNS循环

    最简单的方法是用BIND的DNS循环特性,只要按惯例设置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.
    
    注意,上述看起来似乎是错误的,但事实上,它的确是BIND中一个特意的特性,而且也可以这样用。在解析www.foo.com时,BIND将给出www0-www6的结果(虽然每次在次序上会有轻微的置换/循环),客户端的请求可以被分散到各个服务器。但这并不是一个优秀的负载均衡方案,因为DNS解析信息可以被网络中其他域名服务器缓冲,一旦www.foo.com被解析为wwwN.foo.com,该请求的所有后继请求都将被送往wwwN.foo.com。但是最终结果是正确的,因为请求的总量的确被分散到各个服务器了。
  2. DNS负载均衡

    一种成熟的基于DNS的负载均衡方法是使用lbnamed程序,它是一个Perl程序,并带有若干辅助工具,实现了真正的基于DNS的负载均衡。

  3. 代理吞吐循环(Proxy Throughput Round-Robin)

    这是一个使用mod_rewrite以及代理吞吐特性的方法。首先,在DNS记录中将www0.foo.com固定为www.foo.com,如下:

    www    IN  CNAME   www0.foo.com.
    

    其次,将www0.foo.com转换为一个专职代理服务器,即由这个机器把所有到来的URL通过内部代理分散到另外5个服务器(www1-www5)。为此,必须建立一个规则集,对所有URL调用一个负载均衡脚本lb.pl

    RewriteEngine on
    RewriteMap    lb      prg:/path/to/lb.pl
    RewriteRule   ^/(.+)$ ${lb:$1}           [P,L]
    
    下面是lb.pl的内容:
    #!/path/to/perl
    ## lb.pl -- 负载平衡脚本
    
    $| = 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/
            
            

    高级URL重写指南

    本文是mod_rewrite参考文档的补充材料。阐述在实际应用中如何解决网管所面临的基于URL的典型问题,并详细描述了如何配置URL重写规则集以解决这些问题。

    注意:根据你的服务器配置,有可能必须对这里的例子作些小修改,比如,在额外启用 mod_aliasmod_userdir的情况下要增加 [PT]标志,或者为了适应目录级( .htaccess)的配置而将针对服务器级的规则集进行重写。对一个特定的规则集应该先透彻理解然后再考虑应用,这样才能避免出现问题。
    top

    集群网站的同类URL规划

    描述:
    我们希望在一个Intranet集群网站中,对所有WWW服务器建立一致的URL规划,也就是说,所有的URL(针对每个服务器进行本地配置,因此是独立于各个服务器的)实际上都是 独立于各个服务器的!我们需要的是一个具有独立于各个服务器的一致性的WWW域名空间,即URL不需要包含物理目标服务器,而由集群本身来自动定位物理目标主机。
    解决方案:
    首先,目标服务器的信息来自于(分布式的)包含有用户、组以及实体的外部映射表,其格式形如下:
    user1  server_of_user1
    user2  server_of_user2
    :      :
    
    这些信息被存入 map.xxx-to-host文件。其次,如果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/
    
    top

    结构化的用户主目录

    描述:
    一些拥有几千个用户的网站通常都使用结构化的用户主目录规划,即每个用户主目录位于一个带有特定前缀(比如其用户名的第一个字符)的子目录下: /~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
    
    top

    文件系统的重组

    描述:
    这是一个不加雕琢的例子:一个大量使用目录级规则集以实现平滑的观感,并且从来不用调整数据结构的杀手级的应用。背景:从1992年开始, net.sw存放了我收集的免费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树?我希望在为各种URL运行对应的CGI脚本的时候,用户感觉不到 DATA/目录的存在。方案如下:首先,我把下列配置放在针对每个目录的配置文件里,将公布的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
    
    # 通过"/net.sw/"前缀到达
    RewriteBase   /net.sw/
    
    # 首先将根目录重写到cgi处理脚本
    RewriteRule   ^$                       netsw-home.cgi     [L]
    RewriteRule   ^index/.html$            netsw-home.cgi     [L]
    
    # 当浏览器请求perdir页面时剥去子目录
    RewriteRule   ^.+/(netsw-[^/]+/.+)$    $1                 [L]
    
    # 现在打断对本地文件的重写
    RewriteRule   ^netsw-home/.cgi.*       -                  [L]
    RewriteRule   ^netsw-changes/.cgi.*    -                  [L]
    RewriteRule   ^netsw-search/.cgi.*     -                  [L]
    RewriteRule   ^netsw-tree/.cgi$        -                  [L]
    RewriteRule   ^netsw-about/.html$      -                  [L]
    RewriteRule   ^netsw-img/.*$           -                  [L]
    
    # 任何别的东西都是一个由另一个cgi脚本处理的子目录
    RewriteRule   !^netsw-lsdir/.cgi.*     -                  [C]
    RewriteRule   (.*)                     netsw-lsdir.cgi/$1
    

    阅读提示:

    1. 注意前半部分中的L(最后)标志和非替换部分('-')
    2. 注意后半部分中的!(非)符号和C(链)标志
    3. 注意最后一条规则是全匹配模式
    top

    将失败的URL重定向到其他web服务器

    描述:
    一个常见的问题是如何将对web服务器A的失败请求重定向到服务器B。一般,可以使用借助 ErrorDocument的CGI脚本来解决,此外,还有使用 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类型都有效而且安全的方法,但是对web服务器的性能有不利影响。如果web服务器有一个强大的CPU,那就用这个方法。而在慢速机器上,可以用第一种方法,或者用性能更好的CGI脚本。
    top

    文档访问的多路复用

    描述:
    你知道 http://www.perl.com/CPAN的CPAN(综合Perl存档网络)吗?它实现了一个重定向以提供全世界的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 -- CxAN多路映射表
    ##
    
    de        ftp://ftp.cxan.de/CxAN/
    uk        ftp://ftp.cxan.uk/CxAN/
    com       ftp://ftp.cxan.com/CxAN/
     :
    ##EOF##
    
    top

    内容处理

    依赖于浏览器的内容

    描述:
    有时候有必须提供依赖于浏览器的最佳内容(至少对重要的顶级页面),即对最新的Netscape提供最大化的版本,对Lynx提供最小化的版本,而对其他的浏览器则提供一个一般的版本。
    解决方案:
    对此,内容协商无能为力,因为浏览器不提供那种形式的类型,所以只能在"User-Agent"头上想办法。以下规则集可以完成这个操作:如果"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)功能( [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
    

    通过Intranet取得丢失的数据

    描述:
    这在受防火墙保护的内部网( www2.quux-corp.dom)上保存和维护实际数据,而在Internet上虚拟地运行web服务器( www.quux-corp.dom)。方法是外部服务器在空闲时间从内部服务器取得被请求的数据。
    解决方案:
    首先,必须确保防火墙对内部服务器的保护,并只允许此外部服务器获取数据。对包过滤(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
    
    请按你的实际情况,对上例稍作调整。接着,建立通过代理后台获取丢失数据的重写规则:
    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个服务器)?
    解决方案:
    这个问题有许多可能的解决方案,在此,我们讨论通称为"基于DNS"的方案,和特殊的使用 mod_rewrite的方案
    1. DNS循环

      最简单的方法是用BIND的DNS循环特性,只要按惯例设置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.
      
      注意,上述看起来似乎是错误的,但事实上,它的确是BIND中一个特意的特性,而且也可以这样用。在解析www.foo.com时,BIND将给出www0-www6的结果(虽然每次在次序上会有轻微的置换/循环),客户端的请求可以被分散到各个服务器。但这并不是一个优秀的负载均衡方案,因为DNS解析信息可以被网络中其他域名服务器缓冲,一旦www.foo.com被解析为wwwN.foo.com,该请求的所有后继请求都将被送往wwwN.foo.com。但是最终结果是正确的,因为请求的总量的确被分散到各个服务器了。
    2. DNS负载均衡

      一种成熟的基于DNS的负载均衡方法是使用lbnamed程序,它是一个Perl程序,并带有若干辅助工具,实现了真正的基于DNS的负载均衡。

    3. 代理吞吐循环(Proxy Throughput Round-Robin)

      这是一个使用mod_rewrite以及代理吞吐特性的方法。首先,在DNS记录中将www0.foo.com固定为www.foo.com,如下:

      www    IN  CNAME   www0.foo.com.
      

      其次,将www0.foo.com转换为一个专职代理服务器,即由这个机器把所有到来的URL通过内部代理分散到另外5个服务器(www1-www5)。为此,必须建立一个规则集,对所有URL调用一个负载均衡脚本lb.pl

      RewriteEngine on
      RewriteMap    lb      prg:/path/to/lb.pl
      RewriteRule   ^/(.+)$ ${lb:$1}           [P,L]
      
      下面是lb.pl的内容:
      ___FCKpd___23
      最后的说明:这样有用吗?似乎 www0.foo.com也会超载呀?答案是:没错,它的确会超载,但是它超载的仅仅是简单的代理吞吐请求!所有诸如SSI、CGI、ePerl等的处理完全是由其他机器完成的,这个才是重点。
    4. 硬件/TCP循环

      还有一个硬件解决方案。Cisco有一个叫LocalDirector的东西,实现了TCP/IP层的负载均衡,事实上,它是一个位于网站集群前端的电路级网关。如果你有足够资金而且的确需要高性能的解决方案,那么可以用这个。

    新的MIME类型,新的服务

    描述:
    网上有许多很巧妙的CGI程序,但是用法晦涩,许多网管弃之不用。即使是Apache的MEME类型的动作处理器,也仅仅在CGI程序不需要在其输入中包含 PATH_INFOQUERY_STRINGS时才很好用。首先,配置一种新的后缀为 .scgi的(安全CGI)文件类型,其处理器是很常见的 cgiwrap程序。问题是:如果使用同类URL规划(见上述),而用户宿主目录中的一个文件的URL是 /u/user/foo/bar.scgi,可是 cgiwrap要求的URL的格式是 /~user/foo/bar.scgi/,以下重写规则解决了这个问题:
    RewriteRule ^/[uge]/([^/]+)//.www/(.+)/.scgi(.*) ...
    ... /internal/cgi/user/cgiwrap/~$1/$2.scgi$3  [NS,T=application/x-http-cgi]
    
    另外,假设还需要使用其他程序: wwwlog(显示 access.log中的一个URL子树)和 wwwidx(对一个URL子树运行Glimpse),则必须为两个程序提供URL区域作为其操作对象。比如,对 /u/user/foo/执行 swwidx程序的超链是这样的:
    /internal/cgi/user/swwidx?i=/u/user/foo/
    
    其缺点是,必须 同时硬编码超链中的区域 CGI的路径,如果重组了这个区域,就需要花费大量时间来修改各个超链。
    解决方案:
    用一个特殊的新的URL格式,自动拼装CGI参数:
    RewriteRule   ^/([uge])/([^/]+)(/?.*)//*  /internal/cgi/user/wwwidx?i=/$1/$2$3/
    RewriteRule   ^/([uge])/([^/]+)(/?.*):log /internal/cgi/user/wwwlog?f=/$1/$2$3
    
    现在,这个搜索 /u/user/foo/的超链简化成了:
    HREF="*"
    
    它会在内部被自动转换为
    /internal/cgi/user/wwwidx?i=/u/user/foo/
    
    这样就可以为使用" :log"的超链拼装出调用CGI程序的参数。

    传输过程中的内容协商

    描述:
    这是一个很深奥的功能:动态地生成但静态地发送(从文件系统中读出,然后直接发出去),但是如果它丢失了,则由服务器动态生成。这样,可以静态地提供CGI生成的页面,除非有人(或者是一个计划任务)删除了这些静态页面,而且其内容可以得到更新。
    解决方案:
    以下规则集实现了这个功能:
    RewriteCond %{REQUEST_FILENAME}   !-s
    RewriteRule ^page/.html$          page.cgi   [T=application/x-httpd-cgi,L]
    
    这样,如果 page.html不存在或者文件大小为null ,则对 page.html的请求会导致 page.cgi的运行。其中奥妙在于 page.cgi是一个将输出写入到 page.html的(同时也写入 STDOUT)的CGI脚本。执行完毕,服务器则将 page.html的内容发送出去。如果网管需要强制更新其内容,只须删除 page.html即可(通常由一个计划任务完成)。

    自动更新的文档

    描述:
    建立一个复杂的页面,能够在用编辑器写了一个更新的版本时自动在浏览器上得到刷新,这不是很好吗?这可能吗?
    解决方案:
    这是可行的!这需要综合利用MIME多成分、web服务器的NPH和 mod_rewrite的URL操控特性。首先,建立一个新的URL特性:对在文件系统中需要刷新的所有URL加上" :refresh" 。
    RewriteRule   ^(/[uge]/[^/]+/?.*):refresh  /internal/cgi/apache/nph-refresh?f=$1
    
    现在当我们引用如下URL
    /u/foo/bar/page.html:refresh
    
    时,将导致在内部调用
    /internal/cgi/apache/nph-refresh?f=/u/foo/bar/page.html
    
    接着就是NPH-CGI脚本部分了。虽然,人们常说"将此作为一个练习留给读者",但我还是给出答案了。
    #!/sw/bin/perl
    ##
    ##  nph-refresh -- 用于自动刷新页面的 NPH/CGI 脚本
    ##  Copyright (c) 1997 Ralf S. Engelschall, All Rights Reserved.
    ##
    $| = 1;
    
    # 分解 QUERY_STRING 变量
    @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) = 
                  
                  

    高级URL重写指南

    本文是mod_rewrite参考文档的补充材料。阐述在实际应用中如何解决网管所面临的基于URL的典型问题,并详细描述了如何配置URL重写规则集以解决这些问题。

    注意:根据你的服务器配置,有可能必须对这里的例子作些小修改,比如,在额外启用 mod_aliasmod_userdir的情况下要增加 [PT]标志,或者为了适应目录级( .htaccess)的配置而将针对服务器级的规则集进行重写。对一个特定的规则集应该先透彻理解然后再考虑应用,这样才能避免出现问题。
    top

    集群网站的同类URL规划

    描述:
    我们希望在一个Intranet集群网站中,对所有WWW服务器建立一致的URL规划,也就是说,所有的URL(针对每个服务器进行本地配置,因此是独立于各个服务器的)实际上都是 独立于各个服务器的!我们需要的是一个具有独立于各个服务器的一致性的WWW域名空间,即URL不需要包含物理目标服务器,而由集群本身来自动定位物理目标主机。
    解决方案:
    首先,目标服务器的信息来自于(分布式的)包含有用户、组以及实体的外部映射表,其格式形如下:
    user1  server_of_user1
    user2  server_of_user2
    :      :
    
    这些信息被存入 map.xxx-to-host文件。其次,如果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/
    
    top

    结构化的用户主目录

    描述:
    一些拥有几千个用户的网站通常都使用结构化的用户主目录规划,即每个用户主目录位于一个带有特定前缀(比如其用户名的第一个字符)的子目录下: /~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
    
    top

    文件系统的重组

    描述:
    这是一个不加雕琢的例子:一个大量使用目录级规则集以实现平滑的观感,并且从来不用调整数据结构的杀手级的应用。背景:从1992年开始, net.sw存放了我收集的免费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树?我希望在为各种URL运行对应的CGI脚本的时候,用户感觉不到 DATA/目录的存在。方案如下:首先,我把下列配置放在针对每个目录的配置文件里,将公布的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
    
    # 通过"/net.sw/"前缀到达
    RewriteBase   /net.sw/
    
    # 首先将根目录重写到cgi处理脚本
    RewriteRule   ^$                       netsw-home.cgi     [L]
    RewriteRule   ^index/.html$            netsw-home.cgi     [L]
    
    # 当浏览器请求perdir页面时剥去子目录
    RewriteRule   ^.+/(netsw-[^/]+/.+)$    $1                 [L]
    
    # 现在打断对本地文件的重写
    RewriteRule   ^netsw-home/.cgi.*       -                  [L]
    RewriteRule   ^netsw-changes/.cgi.*    -                  [L]
    RewriteRule   ^netsw-search/.cgi.*     -                  [L]
    RewriteRule   ^netsw-tree/.cgi$        -                  [L]
    RewriteRule   ^netsw-about/.html$      -                  [L]
    RewriteRule   ^netsw-img/.*$           -                  [L]
    
    # 任何别的东西都是一个由另一个cgi脚本处理的子目录
    RewriteRule   !^netsw-lsdir/.cgi.*     -                  [C]
    RewriteRule   (.*)                     netsw-lsdir.cgi/$1
    

    阅读提示:

    1. 注意前半部分中的L(最后)标志和非替换部分('-')
    2. 注意后半部分中的!(非)符号和C(链)标志
    3. 注意最后一条规则是全匹配模式
    top

    将失败的URL重定向到其他web服务器

    描述:
    一个常见的问题是如何将对web服务器A的失败请求重定向到服务器B。一般,可以使用借助 ErrorDocument的CGI脚本来解决,此外,还有使用 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类型都有效而且安全的方法,但是对web服务器的性能有不利影响。如果web服务器有一个强大的CPU,那就用这个方法。而在慢速机器上,可以用第一种方法,或者用性能更好的CGI脚本。
    top

    文档访问的多路复用

    描述:
    你知道 http://www.perl.com/CPAN的CPAN(综合Perl存档网络)吗?它实现了一个重定向以提供全世界的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 -- CxAN多路映射表
    ##
    
    de        ftp://ftp.cxan.de/CxAN/
    uk        ftp://ftp.cxan.uk/CxAN/
    com       ftp://ftp.cxan.com/CxAN/
     :
    ##EOF##
    
    top

    内容处理

    依赖于浏览器的内容

    描述:
    有时候有必须提供依赖于浏览器的最佳内容(至少对重要的顶级页面),即对最新的Netscape提供最大化的版本,对Lynx提供最小化的版本,而对其他的浏览器则提供一个一般的版本。
    解决方案:
    对此,内容协商无能为力,因为浏览器不提供那种形式的类型,所以只能在"User-Agent"头上想办法。以下规则集可以完成这个操作:如果"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)功能( [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
    

    通过Intranet取得丢失的数据

    描述:
    这在受防火墙保护的内部网( www2.quux-corp.dom)上保存和维护实际数据,而在Internet上虚拟地运行web服务器( www.quux-corp.dom)。方法是外部服务器在空闲时间从内部服务器取得被请求的数据。
    解决方案:
    首先,必须确保防火墙对内部服务器的保护,并只允许此外部服务器获取数据。对包过滤(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
    
    请按你的实际情况,对上例稍作调整。接着,建立通过代理后台获取丢失数据的重写规则:
    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个服务器)?
    解决方案:
    这个问题有许多可能的解决方案,在此,我们讨论通称为"基于DNS"的方案,和特殊的使用 mod_rewrite的方案
    1. DNS循环

      最简单的方法是用BIND的DNS循环特性,只要按惯例设置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.
      
      注意,上述看起来似乎是错误的,但事实上,它的确是BIND中一个特意的特性,而且也可以这样用。在解析www.foo.com时,BIND将给出www0-www6的结果(虽然每次在次序上会有轻微的置换/循环),客户端的请求可以被分散到各个服务器。但这并不是一个优秀的负载均衡方案,因为DNS解析信息可以被网络中其他域名服务器缓冲,一旦www.foo.com被解析为wwwN.foo.com,该请求的所有后继请求都将被送往wwwN.foo.com。但是最终结果是正确的,因为请求的总量的确被分散到各个服务器了。
    2. DNS负载均衡

      一种成熟的基于DNS的负载均衡方法是使用lbnamed程序,它是一个Perl程序,并带有若干辅助工具,实现了真正的基于DNS的负载均衡。

    3. 代理吞吐循环(Proxy Throughput Round-Robin)

      这是一个使用mod_rewrite以及代理吞吐特性的方法。首先,在DNS记录中将www0.foo.com固定为www.foo.com,如下:

      www    IN  CNAME   www0.foo.com.
      

      其次,将www0.foo.com转换为一个专职代理服务器,即由这个机器把所有到来的URL通过内部代理分散到另外5个服务器(www1-www5)。为此,必须建立一个规则集,对所有URL调用一个负载均衡脚本lb.pl

      RewriteEngine on
      RewriteMap    lb      prg:/path/to/lb.pl
      RewriteRule   ^/(.+)$ ${lb:$1}           [P,L]
      
      下面是lb.pl的内容:
      #!/path/to/perl
      ## lb.pl -- 负载平衡脚本
      
      $| = 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/
                            
                            

      高级URL重写指南

      本文是mod_rewrite参考文档的补充材料。阐述在实际应用中如何解决网管所面临的基于URL的典型问题,并详细描述了如何配置URL重写规则集以解决这些问题。

      注意:根据你的服务器配置,有可能必须对这里的例子作些小修改,比如,在额外启用 mod_aliasmod_userdir的情况下要增加 [PT]标志,或者为了适应目录级( .htaccess)的配置而将针对服务器级的规则集进行重写。对一个特定的规则集应该先透彻理解然后再考虑应用,这样才能避免出现问题。
      top

      集群网站的同类URL规划

      描述:
      我们希望在一个Intranet集群网站中,对所有WWW服务器建立一致的URL规划,也就是说,所有的URL(针对每个服务器进行本地配置,因此是独立于各个服务器的)实际上都是 独立于各个服务器的!我们需要的是一个具有独立于各个服务器的一致性的WWW域名空间,即URL不需要包含物理目标服务器,而由集群本身来自动定位物理目标主机。
      解决方案:
      首先,目标服务器的信息来自于(分布式的)包含有用户、组以及实体的外部映射表,其格式形如下:
      user1  server_of_user1
      user2  server_of_user2
      :      :
      
      这些信息被存入 map.xxx-to-host文件。其次,如果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/
      
      top

      结构化的用户主目录

      描述:
      一些拥有几千个用户的网站通常都使用结构化的用户主目录规划,即每个用户主目录位于一个带有特定前缀(比如其用户名的第一个字符)的子目录下: /~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
      
      top

      文件系统的重组

      描述:
      这是一个不加雕琢的例子:一个大量使用目录级规则集以实现平滑的观感,并且从来不用调整数据结构的杀手级的应用。背景:从1992年开始, net.sw存放了我收集的免费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树?我希望在为各种URL运行对应的CGI脚本的时候,用户感觉不到 DATA/目录的存在。方案如下:首先,我把下列配置放在针对每个目录的配置文件里,将公布的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
      
      # 通过"/net.sw/"前缀到达
      RewriteBase   /net.sw/
      
      # 首先将根目录重写到cgi处理脚本
      RewriteRule   ^$                       netsw-home.cgi     [L]
      RewriteRule   ^index/.html$            netsw-home.cgi     [L]
      
      # 当浏览器请求perdir页面时剥去子目录
      RewriteRule   ^.+/(netsw-[^/]+/.+)$    $1                 [L]
      
      # 现在打断对本地文件的重写
      RewriteRule   ^netsw-home/.cgi.*       -                  [L]
      RewriteRule   ^netsw-changes/.cgi.*    -                  [L]
      RewriteRule   ^netsw-search/.cgi.*     -                  [L]
      RewriteRule   ^netsw-tree/.cgi$        -                  [L]
      RewriteRule   ^netsw-about/.html$      -                  [L]
      RewriteRule   ^netsw-img/.*$           -                  [L]
      
      # 任何别的东西都是一个由另一个cgi脚本处理的子目录
      RewriteRule   !^netsw-lsdir/.cgi.*     -                  [C]
      RewriteRule   (.*)                     netsw-lsdir.cgi/$1
      

      阅读提示:

      1. 注意前半部分中的L(最后)标志和非替换部分('-')
      2. 注意后半部分中的!(非)符号和C(链)标志
      3. 注意最后一条规则是全匹配模式
      top

      将失败的URL重定向到其他web服务器

      描述:
      一个常见的问题是如何将对web服务器A的失败请求重定向到服务器B。一般,可以使用借助 ErrorDocument的CGI脚本来解决,此外,还有使用 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类型都有效而且安全的方法,但是对web服务器的性能有不利影响。如果web服务器有一个强大的CPU,那就用这个方法。而在慢速机器上,可以用第一种方法,或者用性能更好的CGI脚本。
      top

      文档访问的多路复用

      描述:
      你知道 http://www.perl.com/CPAN的CPAN(综合Perl存档网络)吗?它实现了一个重定向以提供全世界的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 -- CxAN多路映射表
      ##
      
      de        ftp://ftp.cxan.de/CxAN/
      uk        ftp://ftp.cxan.uk/CxAN/
      com       ftp://ftp.cxan.com/CxAN/
       :
      ##EOF##
      
      top

      内容处理

      依赖于浏览器的内容

      描述:
      有时候有必须提供依赖于浏览器的最佳内容(至少对重要的顶级页面),即对最新的Netscape提供最大化的版本,对Lynx提供最小化的版本,而对其他的浏览器则提供一个一般的版本。
      解决方案:
      对此,内容协商无能为力,因为浏览器不提供那种形式的类型,所以只能在"User-Agent"头上想办法。以下规则集可以完成这个操作:如果"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)功能( [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
      

      通过Intranet取得丢失的数据

      描述:
      这在受防火墙保护的内部网( www2.quux-corp.dom)上保存和维护实际数据,而在Internet上虚拟地运行web服务器( www.quux-corp.dom)。方法是外部服务器在空闲时间从内部服务器取得被请求的数据。
      解决方案:
      首先,必须确保防火墙对内部服务器的保护,并只允许此外部服务器获取数据。对包过滤(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
      
      请按你的实际情况,对上例稍作调整。接着,建立通过代理后台获取丢失数据的重写规则:
      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个服务器)?
      解决方案:
      这个问题有许多可能的解决方案,在此,我们讨论通称为"基于DNS"的方案,和特殊的使用 mod_rewrite的方案
      1. DNS循环

        最简单的方法是用BIND的DNS循环特性,只要按惯例设置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.
        
        注意,上述看起来似乎是错误的,但事实上,它的确是BIND中一个特意的特性,而且也可以这样用。在解析www.foo.com时,BIND将给出www0-www6的结果(虽然每次在次序上会有轻微的置换/循环),客户端的请求可以被分散到各个服务器。但这并不是一个优秀的负载均衡方案,因为DNS解析信息可以被网络中其他域名服务器缓冲,一旦www.foo.com被解析为wwwN.foo.com,该请求的所有后继请求都将被送往wwwN.foo.com。但是最终结果是正确的,因为请求的总量的确被分散到各个服务器了。
      2. DNS负载均衡

        一种成熟的基于DNS的负载均衡方法是使用lbnamed程序,它是一个Perl程序,并带有若干辅助工具,实现了真正的基于DNS的负载均衡。

      3. 代理吞吐循环(Proxy Throughput Round-Robin)

        这是一个使用mod_rewrite以及代理吞吐特性的方法。首先,在DNS记录中将www0.foo.com固定为www.foo.com,如下:

        www    IN  CNAME   www0.foo.com.
        

        其次,将www0.foo.com转换为一个专职代理服务器,即由这个机器把所有到来的URL通过内部代理分散到另外5个服务器(www1-www5)。为此,必须建立一个规则集,对所有URL调用一个负载均衡脚本lb.pl

        RewriteEngine on
        RewriteMap    lb      prg:/path/to/lb.pl
        RewriteRule   ^/(.+)$ ${lb:$1}           [P,L]
        
        下面是lb.pl的内容:
        ___FCKpd___23
        最后的说明:这样有用吗?似乎 www0.foo.com也会超载呀?答案是:没错,它的确会超载,但是它超载的仅仅是简单的代理吞吐请求!所有诸如SSI、CGI、ePerl等的处理完全是由其他机器完成的,这个才是重点。
      4. 硬件/TCP循环

        还有一个硬件解决方案。Cisco有一个叫LocalDirector的东西,实现了TCP/IP层的负载均衡,事实上,它是一个位于网站集群前端的电路级网关。如果你有足够资金而且的确需要高性能的解决方案,那么可以用这个。

      新的MIME类型,新的服务

      描述:
      网上有许多很巧妙的CGI程序,但是用法晦涩,许多网管弃之不用。即使是Apache的MEME类型的动作处理器,也仅仅在CGI程序不需要在其输入中包含 PATH_INFOQUERY_STRINGS时才很好用。首先,配置一种新的后缀为 .scgi的(安全CGI)文件类型,其处理器是很常见的 cgiwrap程序。问题是:如果使用同类URL规划(见上述),而用户宿主目录中的一个文件的URL是 /u/user/foo/bar.scgi,可是 cgiwrap要求的URL的格式是 /~user/foo/bar.scgi/,以下重写规则解决了这个问题:
      RewriteRule ^/[uge]/([^/]+)//.www/(.+)/.scgi(.*) ...
      ... /internal/cgi/user/cgiwrap/~$1/$2.scgi$3  [NS,T=application/x-http-cgi]
      
      另外,假设还需要使用其他程序: wwwlog(显示 access.log中的一个URL子树)和 wwwidx(对一个URL子树运行Glimpse),则必须为两个程序提供URL区域作为其操作对象。比如,对 /u/user/foo/执行 swwidx程序的超链是这样的:
      /internal/cgi/user/swwidx?i=/u/user/foo/
      
      其缺点是,必须 同时硬编码超链中的区域 CGI的路径,如果重组了这个区域,就需要花费大量时间来修改各个超链。
      解决方案:
      用一个特殊的新的URL格式,自动拼装CGI参数:
      RewriteRule   ^/([uge])/([^/]+)(/?.*)//*  /internal/cgi/user/wwwidx?i=/$1/$2$3/
      RewriteRule   ^/([uge])/([^/]+)(/?.*):log /internal/cgi/user/wwwlog?f=/$1/$2$3
      
      现在,这个搜索 /u/user/foo/的超链简化成了:
      HREF="*"
      
      它会在内部被自动转换为
      /internal/cgi/user/wwwidx?i=/u/user/foo/
      
      这样就可以为使用" :log"的超链拼装出调用CGI程序的参数。

      传输过程中的内容协商

      描述:
      这是一个很深奥的功能:动态地生成但静态地发送(从文件系统中读出,然后直接发出去),但是如果它丢失了,则由服务器动态生成。这样,可以静态地提供CGI生成的页面,除非有人(或者是一个计划任务)删除了这些静态页面,而且其内容可以得到更新。
      解决方案:
      以下规则集实现了这个功能:
      RewriteCond %{REQUEST_FILENAME}   !-s
      RewriteRule ^page/.html$          page.cgi   [T=application/x-httpd-cgi,L]
      
      这样,如果 page.html不存在或者文件大小为null ,则对 page.html的请求会导致 page.cgi的运行。其中奥妙在于 page.cgi是一个将输出写入到 page.html的(同时也写入 STDOUT)的CGI脚本。执行完毕,服务器则将 page.html的内容发送出去。如果网管需要强制更新其内容,只须删除 page.html即可(通常由一个计划任务完成)。

      自动更新的文档

      描述:
      建立一个复杂的页面,能够在用编辑器写了一个更新的版本时自动在浏览器上得到刷新,这不是很好吗?这可能吗?
      解决方案:
      这是可行的!这需要综合利用MIME多成分、web服务器的NPH和 mod_rewrite的URL操控特性。首先,建立一个新的URL特性:对在文件系统中需要刷新的所有URL加上" :refresh" 。
      RewriteRule   ^(/[uge]/[^/]+/?.*):refresh  /internal/cgi/apache/nph-refresh?f=$1
      
      现在当我们引用如下URL
      /u/foo/bar/page.html:refresh
      
      时,将导致在内部调用
      /internal/cgi/apache/nph-refresh?f=/u/foo/bar/page.html
      
      接着就是NPH-CGI脚本部分了。虽然,人们常说"将此作为一个练习留给读者",但我还是给出答案了。
      ___FCKpd___33

      海量虚拟主机

      描述:
      Apache的 <VirtualHost>功能很强,在有几十个虚拟主机的情况下运行得仍然很好,但是如果你是ISP,需要提供成百上千个虚拟主机,那么这就不是最佳选择了。
      解决方案:
      为此,需要用 代理吞吐(Proxy Throughput)功能( [P]标志)映射远程页面甚至整个远程网络区域到自己的域名空间:
      ##
      ##  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
      ##
          :
      # 在重定向时使用规范化的主机名等等
      UseCanonicalName on
      
          :
      # 在CLF-format之前添加虚拟主机
      CustomLog  /path/to/access_log  "%{VHOST}e %h %l %u %t /"%r/" %>s %b"
          :
      
      # 为主服务器启用重写引擎
      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_URI}  !^/commonurl1/.*
      RewriteCond   %{REQUEST_URI}  !^/commonurl2/.*
          :
      RewriteCond   %{REQUEST_URI}  !^/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

      访问控制

      对主机的拒绝

      描述:
      如何禁止一批外部列表中的主机对我们服务器的访问?
      解决方案:
      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]
      
      ##
      ##  hosts.deny
      ##
      ##  注意! 这是一个映射而不是列表(即使我们这样看待它)。
      ##  mod_rewrite会将它作为 键/值 对进行解析。
      ##  所以每一项至少要存在一个伪值:"-"
      ##
      
      193.102.180.41 -
      bsdti1.sdm.de  -
      192.76.162.40  -
      

      对代理的拒绝

      描述:
      如何拒绝某个主机或者来自特定主机的用户使用Apache代理?
      解决方案:
      首先,要确保Apache配置文件中 mod_rewritemod_proxy的下面!使它在 mod_proxy 之前被调用。然后使用如下方法拒绝某个主机:
      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]
      

      特殊的认证

      描述:
      有时候,会需要一种非常特殊的认证,即对一组明确指定的用户,允许其访问,且没有(在使用 mod_auth_basic的基本认证方法时可能会出现的)任何提示。
      解决方案:
      使用一个重写条件列表来拒绝朋友以外的所有人:
      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的转向器

      描述:
      如何配置一个基于HTTP头"Referer"的转向器以转向到任意数量的引用页(referring page)?
      解决方案:
      使用下面这个很巧妙的规则集:
      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]
      
      并结合相应的重写映射表:
      ##
      ##  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/
      
      它可以自动将请求(在映射表中指定了" -"值的时候)重定向回其引用页(referring page),或者(在映射表中URL有第二个参数时)重定向到一个特定的URL。
        "; } ##EOF##
      最后的说明:这样有用吗?似乎 www0.foo.com也会超载呀?答案是:没错,它的确会超载,但是它超载的仅仅是简单的代理吞吐请求!所有诸如SSI、CGI、ePerl等的处理完全是由其他机器完成的,这个才是重点。
    4. 硬件/TCP循环

      还有一个硬件解决方案。Cisco有一个叫LocalDirector的东西,实现了TCP/IP层的负载均衡,事实上,它是一个位于网站集群前端的电路级网关。如果你有足够资金而且的确需要高性能的解决方案,那么可以用这个。

    新的MIME类型,新的服务

    描述:
    网上有许多很巧妙的CGI程序,但是用法晦涩,许多网管弃之不用。即使是Apache的MEME类型的动作处理器,也仅仅在CGI程序不需要在其输入中包含 PATH_INFOQUERY_STRINGS时才很好用。首先,配置一种新的后缀为 .scgi的(安全CGI)文件类型,其处理器是很常见的 cgiwrap程序。问题是:如果使用同类URL规划(见上述),而用户宿主目录中的一个文件的URL是 /u/user/foo/bar.scgi,可是 cgiwrap要求的URL的格式是 /~user/foo/bar.scgi/,以下重写规则解决了这个问题:
    ___FCKpd___24
    另外,假设还需要使用其他程序: wwwlog(显示 access.log中的一个URL子树)和 wwwidx(对一个URL子树运行Glimpse),则必须为两个程序提供URL区域作为其操作对象。比如,对 /u/user/foo/执行 swwidx程序的超链是这样的:
    ___FCKpd___25
    其缺点是,必须 同时硬编码超链中的区域 CGI的路径,如果重组了这个区域,就需要花费大量时间来修改各个超链。
    解决方案:
    用一个特殊的新的URL格式,自动拼装CGI参数:
    ___FCKpd___26
    现在,这个搜索 /u/user/foo/的超链简化成了:
    ___FCKpd___27
    它会在内部被自动转换为
    ___FCKpd___28
    这样就可以为使用" :log"的超链拼装出调用CGI程序的参数。

    传输过程中的内容协商

    描述:
    这是一个很深奥的功能:动态地生成但静态地发送(从文件系统中读出,然后直接发出去),但是如果它丢失了,则由服务器动态生成。这样,可以静态地提供CGI生成的页面,除非有人(或者是一个计划任务)删除了这些静态页面,而且其内容可以得到更新。
    解决方案:
    以下规则集实现了这个功能:
    ___FCKpd___29
    这样,如果 page.html不存在或者文件大小为null ,则对 page.html的请求会导致 page.cgi的运行。其中奥妙在于 page.cgi是一个将输出写入到 page.html的(同时也写入 STDOUT)的CGI脚本。执行完毕,服务器则将 page.html的内容发送出去。如果网管需要强制更新其内容,只须删除 page.html即可(通常由一个计划任务完成)。

    自动更新的文档

    描述:
    建立一个复杂的页面,能够在用编辑器写了一个更新的版本时自动在浏览器上得到刷新,这不是很好吗?这可能吗?
    解决方案:
    这是可行的!这需要综合利用MIME多成分、web服务器的NPH和 mod_rewrite的URL操控特性。首先,建立一个新的URL特性:对在文件系统中需要刷新的所有URL加上" :refresh" 。
    ___FCKpd___30
    现在当我们引用如下URL
    ___FCKpd___31
    时,将导致在内部调用
    ___FCKpd___32
    接着就是NPH-CGI脚本部分了。虽然,人们常说"将此作为一个练习留给读者",但我还是给出答案了。
    ___FCKpd___33

    海量虚拟主机

    描述:
    Apache的 <VirtualHost>功能很强,在有几十个虚拟主机的情况下运行得仍然很好,但是如果你是ISP,需要提供成百上千个虚拟主机,那么这就不是最佳选择了。
    解决方案:
    为此,需要用 代理吞吐(Proxy Throughput)功能( [P]标志)映射远程页面甚至整个远程网络区域到自己的域名空间:
    ___FCKpd___34
    ___FCKpd___35
    top

    访问控制

    对主机的拒绝

    描述:
    如何禁止一批外部列表中的主机对我们服务器的访问?
    解决方案:
    ___FCKpd___36
    ___FCKpd___37

    对代理的拒绝

    描述:
    如何拒绝某个主机或者来自特定主机的用户使用Apache代理?
    解决方案:
    首先,要确保Apache配置文件中 mod_rewritemod_proxy的下面!使它在 mod_proxy 之前被调用。然后使用如下方法拒绝某个主机:
    ___FCKpd___38
    使用如下方法拒绝user@host-dependent用户:
    ___FCKpd___39

    特殊的认证

    描述:
    有时候,会需要一种非常特殊的认证,即对一组明确指定的用户,允许其访问,且没有(在使用 mod_auth_basic的基本认证方法时可能会出现的)任何提示。
    解决方案:
    使用一个重写条件列表来拒绝朋友以外的所有人:
    ___FCKpd___40

    基于Referer的转向器

    描述:
    如何配置一个基于HTTP头"Referer"的转向器以转向到任意数量的引用页(referring page)?
    解决方案:
    使用下面这个很巧妙的规则集:
    ___FCKpd___41
    并结合相应的重写映射表:
    ___FCKpd___42
    它可以自动将请求(在映射表中指定了" -"值的时候)重定向回其引用页(referring page),或者(在映射表中URL有第二个参数时)重定向到一个特定的URL。
      [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##

    海量虚拟主机

    描述:
    Apache的 <VirtualHost>功能很强,在有几十个虚拟主机的情况下运行得仍然很好,但是如果你是ISP,需要提供成百上千个虚拟主机,那么这就不是最佳选择了。
    解决方案:
    为此,需要用 代理吞吐(Proxy Throughput)功能( [P]标志)映射远程页面甚至整个远程网络区域到自己的域名空间:
    ___FCKpd___34
    ___FCKpd___35
    top

    访问控制

    对主机的拒绝

    描述:
    如何禁止一批外部列表中的主机对我们服务器的访问?
    解决方案:
    ___FCKpd___36
    ___FCKpd___37

    对代理的拒绝

    描述:
    如何拒绝某个主机或者来自特定主机的用户使用Apache代理?
    解决方案:
    首先,要确保Apache配置文件中 mod_rewritemod_proxy的下面!使它在 mod_proxy 之前被调用。然后使用如下方法拒绝某个主机:
    ___FCKpd___38
    使用如下方法拒绝user@host-dependent用户:
    ___FCKpd___39

    特殊的认证

    描述:
    有时候,会需要一种非常特殊的认证,即对一组明确指定的用户,允许其访问,且没有(在使用 mod_auth_basic的基本认证方法时可能会出现的)任何提示。
    解决方案:
    使用一个重写条件列表来拒绝朋友以外的所有人:
    ___FCKpd___40

    基于Referer的转向器

    描述:
    如何配置一个基于HTTP头"Referer"的转向器以转向到任意数量的引用页(referring page)?
    解决方案:
    使用下面这个很巧妙的规则集:
    ___FCKpd___41
    并结合相应的重写映射表:
    ___FCKpd___42
    它可以自动将请求(在映射表中指定了" -"值的时候)重定向回其引用页(referring page),或者(在映射表中URL有第二个参数时)重定向到一个特定的URL。
      "; } ##EOF##
    最后的说明:这样有用吗?似乎 www0.foo.com也会超载呀?答案是:没错,它的确会超载,但是它超载的仅仅是简单的代理吞吐请求!所有诸如SSI、CGI、ePerl等的处理完全是由其他机器完成的,这个才是重点。
  4. 硬件/TCP循环

    还有一个硬件解决方案。Cisco有一个叫LocalDirector的东西,实现了TCP/IP层的负载均衡,事实上,它是一个位于网站集群前端的电路级网关。如果你有足够资金而且的确需要高性能的解决方案,那么可以用这个。

新的MIME类型,新的服务

描述:
网上有许多很巧妙的CGI程序,但是用法晦涩,许多网管弃之不用。即使是Apache的MEME类型的动作处理器,也仅仅在CGI程序不需要在其输入中包含 PATH_INFOQUERY_STRINGS时才很好用。首先,配置一种新的后缀为 .scgi的(安全CGI)文件类型,其处理器是很常见的 cgiwrap程序。问题是:如果使用同类URL规划(见上述),而用户宿主目录中的一个文件的URL是 /u/user/foo/bar.scgi,可是 cgiwrap要求的URL的格式是 /~user/foo/bar.scgi/,以下重写规则解决了这个问题:
___FCKpd___24
另外,假设还需要使用其他程序: wwwlog(显示 access.log中的一个URL子树)和 wwwidx(对一个URL子树运行Glimpse),则必须为两个程序提供URL区域作为其操作对象。比如,对 /u/user/foo/执行 swwidx程序的超链是这样的:
___FCKpd___25
其缺点是,必须 同时硬编码超链中的区域 CGI的路径,如果重组了这个区域,就需要花费大量时间来修改各个超链。
解决方案:
用一个特殊的新的URL格式,自动拼装CGI参数:
___FCKpd___26
现在,这个搜索 /u/user/foo/的超链简化成了:
___FCKpd___27
它会在内部被自动转换为
___FCKpd___28
这样就可以为使用" :log"的超链拼装出调用CGI程序的参数。

传输过程中的内容协商

描述:
这是一个很深奥的功能:动态地生成但静态地发送(从文件系统中读出,然后直接发出去),但是如果它丢失了,则由服务器动态生成。这样,可以静态地提供CGI生成的页面,除非有人(或者是一个计划任务)删除了这些静态页面,而且其内容可以得到更新。
解决方案:
以下规则集实现了这个功能:
___FCKpd___29
这样,如果 page.html不存在或者文件大小为null ,则对 page.html的请求会导致 page.cgi的运行。其中奥妙在于 page.cgi是一个将输出写入到 page.html的(同时也写入 STDOUT)的CGI脚本。执行完毕,服务器则将 page.html的内容发送出去。如果网管需要强制更新其内容,只须删除 page.html即可(通常由一个计划任务完成)。

自动更新的文档

描述:
建立一个复杂的页面,能够在用编辑器写了一个更新的版本时自动在浏览器上得到刷新,这不是很好吗?这可能吗?
解决方案:
这是可行的!这需要综合利用MIME多成分、web服务器的NPH和 mod_rewrite的URL操控特性。首先,建立一个新的URL特性:对在文件系统中需要刷新的所有URL加上" :refresh" 。
___FCKpd___30
现在当我们引用如下URL
___FCKpd___31
时,将导致在内部调用
___FCKpd___32
接着就是NPH-CGI脚本部分了。虽然,人们常说"将此作为一个练习留给读者",但我还是给出答案了。
___FCKpd___33

海量虚拟主机

描述:
Apache的 <VirtualHost>功能很强,在有几十个虚拟主机的情况下运行得仍然很好,但是如果你是ISP,需要提供成百上千个虚拟主机,那么这就不是最佳选择了。
解决方案:
为此,需要用 代理吞吐(Proxy Throughput)功能( [P]标志)映射远程页面甚至整个远程网络区域到自己的域名空间:
___FCKpd___34
___FCKpd___35
top

访问控制

对主机的拒绝

描述:
如何禁止一批外部列表中的主机对我们服务器的访问?
解决方案:
___FCKpd___36
___FCKpd___37

对代理的拒绝

描述:
如何拒绝某个主机或者来自特定主机的用户使用Apache代理?
解决方案:
首先,要确保Apache配置文件中 mod_rewritemod_proxy的下面!使它在 mod_proxy 之前被调用。然后使用如下方法拒绝某个主机:
___FCKpd___38
使用如下方法拒绝user@host-dependent用户:
___FCKpd___39

特殊的认证

描述:
有时候,会需要一种非常特殊的认证,即对一组明确指定的用户,允许其访问,且没有(在使用 mod_auth_basic的基本认证方法时可能会出现的)任何提示。
解决方案:
使用一个重写条件列表来拒绝朋友以外的所有人:
___FCKpd___40

基于Referer的转向器

描述:
如何配置一个基于HTTP头"Referer"的转向器以转向到任意数量的引用页(referring page)?
解决方案:
使用下面这个很巧妙的规则集:
___FCKpd___41
并结合相应的重写映射表:
___FCKpd___42
它可以自动将请求(在映射表中指定了" -"值的时候)重定向回其引用页(referring page),或者(在映射表中URL有第二个参数时)重定向到一个特定的URL。
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Thymeleaf允许在应用程序中配置URL重写过滤器,它通过调用Thymeleaf模板生成的每个URL的Servlet API的javax.servlet.http.HttpServletResponse类中的response.encodeURL()方法来实现。要使用URL重写,需要在Web应用程序中配置一个过滤器,该过滤器将在生成的HTML中重写URL。以下是一个简单的示例,演示如何在Spring Boot应用程序中配置Thymeleaf URL重写过滤器: 1.在pom.xml文件中添加以下依赖项: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> ``` 2.在application.properties文件中添加以下配置: ```properties server.servlet.context-path=/myapp server.tomcat.url-encoding=UTF-8 ``` 3.创建一个名为UrlRewriteFilter的Java类,该类实现了javax.servlet.Filter接口,并在doFilter()方法中调用response.encodeURL()方法: ```java import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class UrlRewriteFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; String url = req.getRequestURI().substring(req.getContextPath().length()); String rewrittenUrl = resp.encodeURL(url); chain.doFilter(request, new UrlRewriteResponseWrapper(resp, rewrittenUrl)); } @Override public void destroy() { } } ``` 4.创建一个名为UrlRewriteResponseWrapper的Java类,该类扩展了javax.servlet.http.HttpServletResponseWrapper类,并重写了encodeURL()方法: ```java import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; import java.io.IOException; public class UrlRewriteResponseWrapper extends HttpServletResponseWrapper { private String rewrittenUrl; public UrlRewriteResponseWrapper(HttpServletResponse response, String rewrittenUrl) { super(response); this.rewrittenUrl = rewrittenUrl; } @Override public String encodeURL(String url) { return rewrittenUrl; } @Override public String encodeRedirectURL(String url) { return rewrittenUrl; } @Override public String encodeUrl(String url) { return rewrittenUrl; } @Override public String encodeRedirectUrl(String url) { return rewrittenUrl; } @Override public void sendRedirect(String location) throws IOException { super.sendRedirect(rewrittenUrl); } } ``` 5.在Spring Boot应用程序的配置类中注册UrlRewriteFilter过滤器: ```java import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AppConfig { @Bean public FilterRegistrationBean<UrlRewriteFilter> urlRewriteFilter() { FilterRegistrationBean<UrlRewriteFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new UrlRewriteFilter()); registrationBean.addUrlPatterns("/*"); return registrationBean; } } ``` 这样,Thymeleaf URL重写过滤器就配置完成了。在Thymeleaf模板中,可以使用th:href属性来生成URL,如下所示: ```html <a th:href="@{/hello}">Hello</a> ``` 这将生成一个相对于应用程序上下文路径的URL,例如/myapp/hello。当用户单击链接时,UrlRewriteFilter过滤器将调用response.encodeURL()方法来重写URL,以便在会话ID中包含JSESSIONID参数(如果需要)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值