Apache HTTP Server 版本 2.4
Apache HTTP Server 通过多个阶段处理请求。在每个阶段,可能都会调用一个或多个模块来处理请求生命周期的该部分。阶段包括 URL 到文件名转换、身份验证、授权、内容和日志记录等。(这不是一个详尽的列表。)
mod_rewrite 在这两个阶段(或通常称为“钩子”)中起作用,以影响 URL 的重写方式。
首先,它使用 URL 到文件名转换钩子,该钩子发生在读取 HTTP 请求后,但在任何授权开始之前。其次,它使用 Fixup 钩子,该钩子发生在授权阶段之后,以及在读取每个目录的配置文件(.htaccess
文件)之后,但在调用内容处理程序之前。
因此,在请求进入并且确定了相应的服务器或虚拟主机之后,重写引擎开始处理出现在每个服务器配置中的任何 mod_rewrite
指令。(即,在主服务器配置文件和 <Virtualhost>
部分中。)这发生在 URL 到文件名阶段。
几个步骤之后,一旦找到最终的数据目录,就会应用每个目录的配置指令(.htaccess
文件和 <Directory>
块)。这发生在 Fixup 阶段。
在这两种情况下,mod_rewrite 都将 REQUEST_URI
重写为新的 URL 或文件名。
在每个目录的上下文中(即,在 .htaccess
文件和 Directory
块中),这些规则是在 URL 已经转换为文件名之后应用的。因此,mod_rewrite 最初比较 RewriteRule
指令的 URL 路径是转换为文件名的完整文件系统路径,其中当前目录路径(包括尾部斜杠)已从前面删除。
举例说明:如果规则位于 /var/www/foo/.htaccess 中,并且正在处理对 /foo/bar/baz 的请求,则类似于 ^bar/baz$ 的表达式将匹配。
如果在每个目录的上下文中进行了替换,则会使用新的 URL 发出新的内部子请求,这将重新启动请求阶段的处理。如果替换是相对路径,则 RewriteBase
指令将确定附加到替换的 URL 路径前缀。在每个目录的上下文中,必须注意创建最终(在每个目录重写处理的某个未来“轮次”中)不会执行替换以避免循环的规则。(有关此问题的进一步讨论,请参阅 RewriteLooping。)
由于在每个目录的上下文中对 URL 进行了进一步的处理,因此您需要注意在该上下文中以不同的方式编写重写规则。特别是,请记住,引导目录路径将从重写规则将看到的 URL 中剥离。请考虑以下示例以进一步说明。
规则位置 | 规则 |
---|---|
VirtualHost 部分 | RewriteRule "^/images/(.+)\.jpg" "/images/$1.gif" |
.htaccess 文件位于文档根目录中 | RewriteRule "^images/(.+)\.jpg" "images/$1.gif" |
.htaccess 文件位于 images 目录中 | RewriteRule "^(.+)\.jpg" "$1.gif" |
要更深入地了解 mod_rewrite 如何在不同上下文中处理 URL,您应该查阅重写期间生成的 日志条目。
现在,当 mod_rewrite 在这两个 API 阶段被触发时,它会从其配置结构中读取配置的规则集(该结构本身是在启动时为每个服务器上下文创建的,或者是在 Apache 内核的目录遍历期间为每个目录上下文创建的)。然后,使用包含的规则集(一个或多个规则及其条件)启动 URL 重写引擎。URL 重写引擎本身的操作对于两种配置上下文都是完全相同的。只有最终的结果处理不同。
规则集中的规则顺序很重要,因为重写引擎以特殊(且不太明显)的顺序处理它们。规则如下:重写引擎逐条规则循环遍历规则集 (RewriteRule
指令),当特定规则匹配时,它可以选择性地循环遍历现有的相应条件(RewriteCond
指令)。出于历史原因,条件首先给出,因此控制流有点冗长。有关更多详细信息,请参见图 1。
图 1:通过重写规则集的控制流
首先,将 URL 与每个规则的 模式 匹配。如果失败,mod_rewrite 会立即停止处理此规则,并继续处理下一条规则。如果 模式 匹配,mod_rewrite 会查找相应的规则条件(RewriteCond 指令,出现在配置中的 RewriteRule 正上方)。如果不存在,它会使用从字符串 替换 构造的新值替换 URL,并继续其规则循环。但是,如果存在条件,它会启动一个内部循环来按列出的顺序处理它们。对于条件,逻辑不同:我们不会将模式与当前 URL 匹配。相反,我们首先通过扩展变量、反向引用、映射查找等创建字符串 测试字符串,然后尝试将 条件模式 与其匹配。如果模式不匹配,则整个条件集和相应的规则将失败。如果模式匹配,则处理下一个条件,直到没有更多条件可用。如果所有条件都匹配,则继续使用 替换 替换 URL。