Apache HTTP Server 版本 2.4
本文档是对 mod_rewrite
参考文档 的补充。它描述了使用 mod_rewrite
所需的基本概念。其他文档将更详细地介绍,但本文档应该可以帮助初学者入门。
Apache 模块 mod_rewrite
是一个非常强大且复杂的模块,它提供了一种执行 URL 操作的方法。使用它,您可以执行几乎所有类型的 URL 重写,这些重写可能是您需要的。但是,它有些复杂,对于初学者来说可能令人生畏。还有一种趋势是将重写规则视为魔术咒语,在不真正了解它们的作用的情况下使用它们。
本文档试图提供足够的背景知识,以便理解后续内容,而不是仅仅盲目地复制。
请记住,许多常见的 URL 操作任务不需要 mod_rewrite
的全部功能和复杂性。对于简单的任务,请参阅 mod_alias
和有关 将 URL 映射到文件系统 的文档。
最后,在继续之前,请确保使用 LogLevel
指令将 mod_rewrite
的日志级别配置为跟踪级别之一。尽管这可能会提供大量信息,但在调试 mod_rewrite
配置问题时,它是必不可少的,因为它会准确地告诉您每个规则是如何处理的。
mod_rewrite
使用 Perl 兼容正则表达式 词汇。在本文件中,我们不会尝试提供正则表达式的详细参考。为此,我们推荐 PCRE 手册页、Perl 正则表达式手册页 和 Mastering Regular Expressions,作者:Jeffrey Friedl。
在本文件中,我们试图提供足够的正则表达式词汇,以帮助您入门,而不是让您不知所措,希望 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
的测试字符串参数。
RewriteRule
模式中的捕获(反直觉地)可用于所有前面的 RewriteCond
指令,因为 RewriteRule
表达式在各个条件之前进行评估。
图 1 显示了反向引用被传输到哪些位置以进行扩展,以及说明了 RewriteRule、RewriteCond 匹配的流程。在接下来的章节中,我们将探讨如何使用这些反向引用,所以如果一开始看起来有点陌生,请不要担心。
图 1: 通过规则的反向引用流。
在此示例中,对 /test/1234
的请求将被转换为 /admin.foo?page=test&id=1234&host=admin.example.com
。
一个 RewriteRule
由三个用空格分隔的参数组成。这些参数是
模式 是一个 正则表达式。它最初(对于第一个重写规则或直到发生替换)与传入请求的 URL 路径(主机名之后但任何表示查询字符串开头的问号之前的部分)匹配,或者在每个目录上下文中,与请求的路径匹配相对于定义该规则的目录。一旦发生替换,后续规则将与替换的值匹配。
图 2: RewriteRule 指令的语法。
替换 本身可以是以下三种情况之一
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/
作为根级目录。)替换 还可以包含对传入 URL 路径中由 模式 匹配的部分的反向引用。考虑以下内容
RewriteRule "^/product/(.*)/view$" "/var/web/productdb/$1"
变量 $1
将被替换为 模式 中括号内的表达式匹配的任何文本。例如,对 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
中包含的正则表达式中的匹配项可以使用变量 %1
、%2
等作为 RewriteRule
中的 替换 的一部分。例如,这将根据用于访问站点的 hostname 将请求定向到不同的目录
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
来确保请求被正确映射。