初级URL重写指南
优质
小牛编辑
138浏览
2023-12-01
本文是mod_rewrite
参考文档的补充材料。阐述在实际应用中如何解决网管所面临的基于URL的典型问题,并详细描述了如何配置URL重写规则集以解决这些问题。
mod_alias
和mod_userdir
的情况下要增加[PT]
标志,或者为了适应目录级(.htaccess
)的配置而将针对服务器级的规则集进行重写。对一个特定的规则集应该先透彻理解然后再考虑应用,这样才能避免出现问题。规范化URL
- 描述:
- 在有些web服务器上,一个资源会拥有多个URL。在实际应用和发布中应该使用的是规范的URL,其他的则是简写或者只在内部使用。无论用户在请求中使用什么形式的URL,最终看见的都应该是规范的URL。
- 解决方案:
- 对所有不规范的URL执行一个外部HTTP重定向,以改变它在浏览器地址栏中的显示及其后继请求。下例中的规则集用规范的
/u/user
替换/~user
,并修正了/u/user
所遗漏的后缀斜杠。RewriteRule ^/~([^/]+)/?(.*) /u/$1/$2 [R] RewriteRule ^/u/([^/]+)$ /$1/$2/ [R]
规范化主机名
- 描述:
- 这个规则的目的是强制使用特定的主机名以代替其他名字。比如,你想强制使用www.example.com代替example.com,就可以在以下方案的基础上进行修改:
- 解决方案:
对运行在非80端口的站点
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]
对运行在80端口的站点
RewriteCond %{HTTP_HOST} !^fully\.qualified\.domain\.name [NC] RewriteCond %{HTTP_HOST} !^$ RewriteRule ^/(.*) http://fully.qualified.domain.name/$1 [L,R]
移动过的DocumentRoot
- 描述:
- 通常,web服务器的
DocumentRoot
直接对应于URL"/
",但是它常常不是处于最高的一级。比如,你希望访问者在进入网站时首先进入/about/
目录。可以使用下面给出的规则集。 - 解决方案:
- 只需将"
/
"重定向到"/about/
"即可:RewriteEngine on RewriteRule ^/$ /about/ [R]
也可以使用
RedirectMatch
指令解决问题:RedirectMatch ^/$ http://example.com/e/www/
结尾斜杠问题
- 描述:
- 每个网管对引用目录的结尾斜杠问题都有一本苦经,如果遗漏了,服务器会产生一个错误,因为如果请求是"/~quux/foo"而不是"/~quux/foo/",服务器就会去找一个叫foo的文件,而它是一个目录,所以就报错了。通常,可以使用这个FAQ entry里面提到的方法解决问题。但是有时候需要使用重写规则来解决问题,比如,在应用了许多复杂的重写规则之后。
- 解决方案:
- 解决这个微妙问题的方案是让服务器自动添加后缀斜杠。为了达到目的,必须使用一个外部重定向,以使浏览器能够正确地处理后继的请求(比如对图片的请求)。如果仅仅执行一个内部重写,可能仅仅对目录页面有效,而对含有相对URL的图片的页面无效,因为浏览器有请求内嵌目标的可能。比如,如果不用外部重定向,对
/~quux/foo/index"
/~user/anypath
"到"http://newserver/~user/anypath
":RewriteEngine on RewriteRule ^/~(.+) http://newserver/~$1 [R,L]
在多个目录中搜索页面
- 描述:
- 有时会有必要使web服务器在多个目录中搜索页面,对此,MultiViews或者其他技术无能为力。
- 解决方案:
- 编制一个明确的规则集以搜索目录中的文件:
RewriteEngine on # 首先尝试在 dir1 中寻找,找到即停 RewriteCond /your/docroot/dir1/%{REQUEST_FILENAME} -f RewriteRule ^(.+) /your/docroot/dir1/$1 [L] # 然后尝试在 dir2 中寻找,找到即停 RewriteCond /your/docroot/dir2/%{REQUEST_FILENAME} -f RewriteRule ^(.+) /your/docroot/dir2/$1 [L] # 再找不到就继续寻找其他的 Alias 或 ScriptAlias 目录 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 A记录的方法实现。 - 解决方案:
- 仅能对包含"Host: "头的HTTP/1.1请求实现。可以使用以下规则集内部地将
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到另一个web服务器www.somewhere.com
,有时这种做法也会用在虚拟主机的配置段中。 - 解决方案:
- 只须一个重写条件:
RewriteEngine on RewriteCond %{REMOTE_HOST} !^.+\.ourdomain\.com$ RewriteRule ^(/~.+) http://www.somewhere.com/$1 [R,L]
重定向锚
- 描述:
- 默认情况下,重定向到一个HTML锚是不可行的,因为'
#
'会被转义为'%23
'。This, in turn, breaks the redirection. - 解决方案:
- 在
RewriteRule
指令中使用[NE]
标志(不转义)。
依赖于时间的重写
- 描述:
- 在页面内容需要按时间的不同而变化的场合,比如重定向特定页面等,许多网管仍然采用CGI脚本的方法,如何用
mod_rewrite
来实现呢? - 解决方案:
- 有许多名为
TIME_xxx
的变量可以用在重写条件中,联合使用词典模式的"<STRING
", "=STRING
", ">STRING
"比较,就可以实现依赖于时间的重写:RewriteEngine on RewriteCond %{TIME_HOUR}%{TIME_MIN} >0700 RewriteCond %{TIME_HOUR}%{TIME_MIN} <1900 RewriteRule ^foo\"Robot排除协议"行,但不足以阻止此类Robot。
- 解决方案:
- 可以用一个规则集以拒绝对网络区域
/~quux/foo/arc/
(对一个很深的目录区域进行列表可能会使服务器产生很大的负载)的访问。还必须确保仅阻止特定的Robot,也就是说,仅仅阻止Robot访问主机是不够的(这样会同时阻止用户访问该主机)。为此,就需要对HTTP头的User-Agent信息作匹配。RewriteCond %{HTTP_USER_AGENT} ^NameOfBadRobot.* RewriteCond %{REMOTE_ADDR} ^123\.45\.67\.[8-9]$ RewriteRule ^/~quux/foo/arc/.+ - [F]
阻止内嵌的图片
- 描述:
- 假设
http://www.quux-corp.de/~quux/
有一些内嵌GIF图片的页面,这些图片很好,所以就有人盗链到他们自己的页面中了。由于这样徒然增加了我们服务器的流量,因此,我们不愿意这种事情发生。 - 解决方案:
- 虽然,我们不能100%地保护这些图片不被写入别人的页面,但至少可以对发出HTTP Referer头的浏览器加以限制。
RewriteCond %{HTTP_REFERER} !^$ RewriteCond %{HTTP_REFERER} !^http://www.quux-corp.de/~quux/.*$ [NC] RewriteRule .*\.gif$ - [F]
RewriteCond %{HTTP_REFERER} !^$ RewriteCond %{HTTP_REFERER} !.*/foo-with-gif\"https://www.xnip.cn/doc/apache22/mod/mod_rewrite">mod_rewrite位于
mod_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_rewrite
解决的FOO/BAR/QUUX/之类的问题? - 解决方案:
- 可以使用一个与
RewriteMap
功能相同的外部RewriteMap
程序,一旦它在Apache启动时被执行,则从STDIN
接收被请求的URL ,并将处理过(通常是重写过的)的URL(以相同顺序)在STDOUT
输出。RewriteEngine on RewriteMap quux-map prg:/path/to/map.quux.pl RewriteRule ^/~quux/(.*)$ /~quux/${quux-map:$1}
#!/path/to/perl # 禁止使用会导致Apache陷入死循环的I/O缓冲 $| = 1; # 从stdin读取URL(每行一个),并在stdout输出替换URL while (<>) { s|^foo/|bar/|; print $_; }
这是只是一个简单的示例,只是把所有的/~quux/foo/...
重写为/~quux/bar/...
而已。但事实上,可以把它修改成任何你想要的输出。但是要注意,虽然一般用户都可以使用,可是只有系统管理员才可以定义这样的映射。