Apache HTTP 服务器版本 2.5

本文档是 mod_rewrite
参考文档的补充。它描述了使用
mod_rewrite 所需的基本概念。
其他文档会更详细地介绍,但本文档应该能帮助初学者入门。
Apache 模块 mod_rewrite 是一个非常强大且复杂的模块,
它提供了进行 URL 操作的方法。使用它,你几乎可以完成所有类型的 URL 重写。
然而,它有一定的复杂性,可能会令初学者望而生畏。
还有一种倾向是将重写规则视为魔法咒语,在不真正理解其作用的情况下使用它们。
本文档试图提供足够的背景知识,使接下来的内容被理解, 而不仅仅是盲目复制。
请记住,许多常见的 URL 操作任务不需要 mod_rewrite
的全部功能和复杂性。对于简单任务,请参阅 mod_alias
和关于将 URL 映射到文件系统的文档。
最后,在继续之前,请务必使用
LogLevel
指令将 mod_rewrite 的日志级别配置为 trace 级别之一。
虽然这可能会产生大量信息,但在调试 mod_rewrite
配置问题时它是不可或缺的,因为它会告诉你每条规则是如何被处理的。
mod_rewrite 使用 Perl
兼容正则表达式词汇。在本文档中,我们不试图提供正则表达式的详细参考。
为此,我们推荐 PCRE 手册页、
Perl 正则表达式手册页
以及 Jeffrey
Friedl 的《精通正则表达式》(第三版出版于 2006 年,
但正则表达式语法基本没有变化,它仍然是该主题的权威参考)。
在本文档中,我们试图提供足够的正则表达式词汇来帮助你入门,
而不会让你感到不知所措,希望
RewriteRule
是科学公式,而不是魔法咒语。
以下是编写正则表达式和
RewriteRule
所需的最基本构建块。它们当然不代表完整的正则表达式词汇,
但它们是一个好的起点,应该能帮助你阅读基本的正则表达式并编写自己的正则表达式。
| 字符 | 含义 | 示例 |
|---|---|---|
. |
匹配任意单个字符 | c.t 将匹配 cat、cot、
cut 等 |
+ |
重复前一个匹配一次或多次 | a+ 匹配 a、aa、
aaa 等 |
* |
重复前一个匹配零次或多次 | a* 匹配 a+ 匹配的所有内容,
但也会匹配空字符串 |
? |
使匹配变为可选 | colou?r 将匹配 color 和
colour |
\ |
转义下一个字符 | \. 将匹配 .(点)而不是如上所述的
任意单个字符 |
^ |
称为锚点,匹配字符串的开头 | ^a 匹配以 a 开头的字符串 |
$ |
另一个锚点,匹配字符串的结尾 | a$ 匹配以 a 结尾的字符串 |
( ) |
将多个字符组合为一个单元,并捕获匹配以用于反向引用 | (ab)+ 匹配 ababab——即
+ 应用于整个组。关于反向引用的更多信息见
下文 |
[ ] |
字符类——匹配其中一个字符 | c[uoa]t 匹配 cut、cot 或
cat |
[^ ] |
否定字符类——匹配未指定的任意字符 | c[^/]t 匹配 cat 或 c=t
但不匹配 c/t |
在 mod_rewrite 中,!
字符可以在正则表达式前使用来否定它。也就是说,
只有当字符串不匹配表达式的其余部分时,才认为匹配成功。
这里有一个重要的事情要记住:每当你在模式或某个
CondPattern 中使用括号时,都会在内部创建反向引用,
可以使用字符串 $N 和 %N(见下文)来引用。
这些可用于创建
RewriteRule 的
替换参数或
RewriteCond 的
TestString 参数。
RewriteRule
模式中的捕获(看似违反直觉地)可供所有前面的
RewriteCond 指令使用,
因为 RewriteRule
表达式在各个条件之前就已被求值。
图 1 显示了反向引用被传递到哪些位置进行展开, 并说明了 RewriteRule 和 RewriteCond 匹配的流程。 在接下来的章节中,我们将探索如何使用这些反向引用, 所以如果你一开始觉得有些陌生,不必担心。

图 1:反向引用在规则中的流向。
在此示例中,对 /test/1234 的请求将被转换为 /admin.foo?page=test&id=1234&host=admin.example.com。
RewriteRule
由三个以空格分隔的参数组成。这些参数是:
Pattern 是一个正则表达式。 它最初(对于第一条重写规则或直到发生替换为止)与传入请求的 URL 路径 (主机名之后但问号之前的部分,问号表示查询字符串的开始)进行匹配, 或者在目录级上下文中与请求相对于定义规则的目录的路径进行匹配。 一旦发生替换,后续规则将与替换后的值进行匹配。

图 2:RewriteRule 指令的语法。
Substitution 本身可以是以下三种之一:
RewriteRule "^/games" "/usr/local/games/web/puzzles.html"
这将请求映射到文件系统上的任意位置,
类似于 Alias 指令。
RewriteRule "^/games$" "/puzzles.html"
如果 DocumentRoot 设置为
/usr/local/apache2/htdocs,则此指令会将对
http://example.com/games 的请求映射到路径
/usr/local/apache2/htdocs/puzzles.html。
RewriteRule "^/product/view$" "http://site2.example.com/seeproduct.html" [R]
这告诉客户端对指定 URL 发出新的请求。
/usr/)存在于文件系统上,
而对于 2,它不存在。
(即文件系统中没有 /bar/ 作为根级目录。)Substitution 还可以包含对传入 URL 路径中由 Pattern 匹配的部分的反向引用。 请看以下示例:
RewriteRule "^/product/(.*)/view$" "/var/web/productdb/$1"
变量 $1 将被替换为 Pattern
中括号内的表达式所匹配的任何文本。例如,对
http://example.com/product/r14df/view 的请求将被映射到路径
/var/web/productdb/r14df。
如果括号中有多个表达式,它们将按顺序出现在变量
$1、$2、$3 等中。
RewriteRule
的行为可以通过在规则末尾应用一个或多个标志来修改。
例如,可以通过应用 [NC]
标志使规则的匹配行为不区分大小写:
RewriteRule "^puppy.html" "smalldog.html" [NC]
有关可用标志、其含义和示例的更多详细信息, 请参阅重写标志文档。
一个或多个 RewriteCond
指令可用于限制将受后续
RewriteRule 影响的请求类型。
第一个参数是描述请求特征的变量,
第二个参数是必须匹配该变量的正则表达式,
第三个可选参数是修改匹配评估方式的标志列表。

图 3:RewriteCond 指令的语法
例如,要将来自特定 IP 范围的所有请求发送到不同的服务器, 你可以使用:
RewriteCond "%{REMOTE_ADDR}" "^10\.2\."
RewriteRule "(.*)" "http://intranet.example.com$1"
当指定了多个 RewriteCond 时,
它们必须全部匹配才能应用
RewriteRule。
例如,要拒绝查询字符串中包含"hack"一词的请求,
除非它们还包含含有"go"一词的 cookie,你可以使用:
RewriteCond "%{QUERY_STRING}" "hack"
RewriteCond "%{HTTP_COOKIE}" !go
RewriteRule "." "-" [F]
请注意,感叹号指定否定匹配, 因此只有当 cookie 不包含"go"时才应用该规则。
RewriteCond
中正则表达式的匹配结果可以作为
RewriteRule 中
Substitution 的一部分,使用变量 %1、
%2 等。例如,这将根据用于访问站点的主机名
将请求定向到不同的目录:
RewriteCond "%{HTTP_HOST}" "(.*)"
RewriteRule "^/(.*)" "/sites/%1/$1"
如果请求是 http://example.com/foo/bar,
则 %1 将包含 example.com,
$1 将包含 foo/bar。
RewriteMap
指令提供了一种调用外部函数来为你执行重写的方法。
这在 RewriteMap 补充文档中有更详细的讨论。
重写通常在主服务器配置中(在任何
<Directory>
配置段之外)或在
<VirtualHost>
容器中配置。这是执行重写的最简单方式,也是推荐的方式。
但是,也可以在
<Directory>
配置段或 .htaccess
文件中执行重写,但会增加一些额外的复杂性。
这种技术称为目录级重写。
与服务器级重写的主要区别在于,
包含 .htaccess 文件的目录路径前缀在
RewriteRule
中匹配之前会被去除。此外,应使用
RewriteBase
来确保请求被正确映射。