想问一下CU博客的二级域名功能如何实现
非常简单,apache的重写功能可以轻而易举的实现。
高级URL重写指南
本文是mod_rewrite参考文档的补充材料。阐述在实际应用中如何解决网管所面临的基于URL的典型问题,并详细描述了如何配置URL重写规则集以解决这些问题。
注意:根据你的服务器配置,有可能必须对这里的例子作些小修改,比如,在额外启用mod_alias和mod_userdir的情况下要增加[PT]标志,或者为了适应目录级(.htaccess)的配置而将针对服务器级的规则集进行重写。对一个特定的规则集应该先透彻理解然后再考虑应用,这样才能避免出现问题。
集群网站的同类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
重定向到
以下规则集依靠映射文件来完成这个操作(如果一个用户在映射表中没有对应的项,则使用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/([^/]+)/?(.*)
RewriteRule ^/g/([^/]+)/?(.*)
RewriteRule ^/e/([^/]+)/?(.*)
RewriteRule ^/([uge])/([^/]+)/?$ /$1/$2/.www/
RewriteRule ^/([uge])/([^/]+)/([^.]+.+) /$1/$2/.www/$3\
结构化的用户主目录
描述:
一些拥有几千个用户的网站通常都使用结构化的用户主目录规划,即每个用户主目录位于一个带有特定前缀(比如其用户名的第一个字符)的子目录下:/~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
文件系统的重组
描述:
这是一个不加雕琢的例子:一个大量使用目录级规则集以实现平滑的观感,并且从来不用调整数据结构的杀手级的应用。背景:从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
阅读提示:
注意前半部分中的L(最后)标志和非替换部分('-')
注意后半部分中的!(非)符号和C(链)标志
注意最后一条规则是全匹配模式
将失败的URL重定向到其他web服务器
描述:
一个常见的问题是如何将对web服务器A的失败请求重定向到服务器B。一般,可以使用借助ErrorDocument的CGI脚本来解决,此外,还有使用mod_rewrite的方案。但是须注意,这种方法的执行效率不如使用ErrorDocument的CGI脚本!
解决方案:
第一种方案,有最好的性能而灵活性欠佳,出错概率小所以安全:
RewriteEngine on
RewriteCond /your/docroot/%{REQUEST_FILENAME} !-f
RewriteRule ^(.+)
但是问题在于,它只对位于DocumentRoot中的页面有效。虽然可以增加更多的条件(比如同时还处理用户主目录,等等),但是还有一个更好的方法:
RewriteEngine on
RewriteCond %{REQUEST_URI} !-U
RewriteRule ^(.+)
这种方法使用了mod_rewrite提供的"前瞻"(look-ahead)的功能,是一种对所有URL类型都有效而且安全的方法,但是对web服务器的性能有不利影响。如果web服务器有一个强大的CPU,那就用这个方法。而在慢速机器上,可以用第一种方法,或者用性能更好的CGI脚本。
文档访问的多路复用
描述:
你知道的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
uk
com
:
##EOF##
内容处理
依赖于浏览器的内容
描述:
有时候有必须提供依赖于浏览器的最佳内容(至少对重要的顶级页面),即对最新的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/(.*)$ [P]
RewriteEngine on
RewriteBase /~quux/
RewriteRule ^usa-news\.html$ [P]
反向动态镜像
描述:
...
解决方案:
RewriteEngine on
RewriteCond /mirror/of/remotesite/$1 -U
RewriteRule ^\.remotesite\.com/(.*)$ /mirror/of/remotesite/$1
通过Intranet取得丢失的数据
描述:
这在受防火墙保护的内部网(www2.quux-corp.dom)上保存和维护实际数据,而在Internet上虚拟地运行web服务器()。方法是外部服务器在空闲时间从内部服务器取得被请求的数据。
解决方案:
首先,必须确保防火墙对内部服务器的保护,并只允许此外部服务器获取数据。对包过滤(packet-filtering)防火墙,可以制定如下防火墙规则:
ALLOW Host 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/?(.*) [P]
负载均衡
描述:
如何将的负载均衡到www[0-5].foo.com(一共是6个服务器)?
解决方案:
这个问题有许多可能的解决方案,在此,我们讨论通称为"基于DNS"的方案,和特殊的使用mod_rewrite的方案
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中一个特意的特性,而且也可以这样用。在解析时,BIND将给出www0-www6的结果(虽然每次在次序上会有轻微的置换/循环),客户端的请求可以被分散到各个服务器。但这并不是一个优秀的负载均衡方案,因为DNS解析信息可以被网络中其他域名服务器缓冲,一旦被解析为wwwN.foo.com,该请求的所有后继请求都将被送往wwwN.foo.com。但是最终结果是正确的,因为请求的总量的确被分散到各个服务器了。
DNS负载均衡
一种成熟的基于DNS的负载均衡方法是使用lbnamed程序,它是一个Perl程序,并带有若干辅助工具,实现了真正的基于DNS的负载均衡。
代理吞吐循环(Proxy Throughput Round-Robin)
这是一个使用mod_rewrite以及代理吞吐特性的方法。首先,在DNS记录中将www0.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 () {
$cnt = (($cnt+1) % ($last+1-$first));
$server = sprintf("%s%d.%s", $name, $cnt+$first, $domain);
print "_";
}
##EOF##
最后的说明:这样有用吗?似乎www0.foo.com也会超载呀?答案是:没错,它的确会超载,但是它超载的仅仅是简单的代理吞吐请求!所有诸如SSI、CGI、ePerl等的处理完全是由其他机器完成的,这个才是重点。
硬件/TCP循环
还有一个硬件解决方案。Cisco有一个叫LocalDirector的东西,实现了TCP/IP层的负载均衡,事实上,它是一个位于网站集群前端的电路级网关。如果你有足够资金而且的确需要高性能的解决方案,那么可以用这个。
新的MIME类型,新的服务
描述:
网上有许多很巧妙的CGI程序,但是用法晦涩,许多网管弃之不用。即使是Apache的MEME类型的动作处理器,也仅仅在CGI程序不需要在其输入中包含PATH_INFO和QUERY_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 "<b>ERROR</b>: No file given\n";
exit(0);
}
if (! -f $QS_f) {
print "HTTP/1.0 200 OK\n";
print "Content-type: text/html\n\n";
print "<b>ERROR</b>: File $QS_f not found\n";
exit(0);
}
sub print_http_headers_multipart_begin {
print "HTTP/1.0 200 OK\n";
$bound = "ThisRandomString12345";
print "Content-type: multipart/x-mixed-replace;boundary=$bound\n";
&print_http_headers_multipart_next;
}
sub print_http_headers_multipart_next {
print "\n--$bound\n";
}
sub print_http_headers_multipart_end {
print "\n--$bound--\n";
}
sub displayhtml {
local($buffer) = @_;
$len = length($buffer);
print "Content-type: text/html\n";
print "Content-length: $len\n\n";
print $buffer;
}
sub readfile {
local($file) = @_;
local(*FP, $size, $buffer, $bytes);
($x, $x, $x, $x, $x, $x, $x, $size) = stat($file);
$size = sprintf("%d", $size);
open(FP, "<$file");
$bytes = sysread(FP, $buffer, $size);
close(FP);
return $buffer;
}
$buffer = &readfile($QS_f);
&print_http_headers_multipart_begin;
&displayhtml($buffer);
sub mystat {
local($file) = $_[0];
local($time);
($x, $x, $x, $x, $x, $x, $x, $x, $x, $mtime) = stat($file);
return $mtime;
}
$mtimeL = &mystat($QS_f);
$mtime = $mtime;
for ($n = 0; $n < $QS_n; $n++) {
while (1) {
$mtime = &mystat($QS_f);
if ($mtime ne $mtimeL) {
$mtimeL = $mtime;
sleep(2);
$buffer = &readfile($QS_f);
&print_http_headers_multipart_next;
&displayhtml($buffer);
sleep(5);
$mtimeL = &mystat($QS_f);
last;
}
sleep($QS_s);
}
}
&print_http_headers_multipart_end;
exit(0);
##EOF##
海量虚拟主机
描述:
Apache的功能很强,在有几十个虚拟主机的情况下运行得仍然很好,但是如果你是ISP,需要提供成百上千个虚拟主机,那么这就不是最佳选择了。
解决方案:
为此,需要用代理吞吐(Proxy Throughput)功能([P]标志)映射远程页面甚至整个远程网络区域到自己的域名空间:
##
## vhost.map
##
/path/to/docroot/vhost1
/path/to/docroot/vhost2
:
/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}}]
:
访问控制
对主机的拒绝
描述:
如何禁止一批外部列表中的主机对我们服务器的访问?
解决方案:
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_rewrite在mod_proxy的下面!使它在mod_proxy之前被调用。然后使用如下方法拒绝某个主机:
RewriteCond %{REMOTE_HOST} ^badhost\.mydomain\.com$
RewriteRule !^http://[^/.]\.mydomain.com.* - [F]
使用如下方法拒绝用户:
RewriteCond }
RewriteRule !^http://[^/.]\.mydomain.com.* - [F]
特殊的认证
描述:
有时候,会需要一种非常特殊的认证,即对一组明确指定的用户,允许其访问,且没有(在使用mod_auth_basic的基本认证方法时可能会出现的)任何提示。
解决方案:
使用一个重写条件列表来拒绝朋友以外的所有人:
RewriteCond }
RewriteCond }
RewriteCond }
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
##
-
-
它可以自动将请求(在映射表中指定了"-"值的时候)重定向回其引用页(referring page),或者(在映射表中URL有第二个参数时)重定向到一个特定的URL。