Apache HTTP 服务器版本 2.5

本文档讨论了 RewriteRule
指令可用的标志,并提供了详细的说明和示例。
介绍
B(转义反向引用)
BNP|backrefnoplus(不将空格转义为 +)
BCTLS
BNE
C|chain
CO|cookie
DPI|discardpath
E|env
END
F|forbidden
G|gone
H|handler
L|last
N|next
NC|nocase
NE|noescape
NS|nosubreq
P|proxy
PT|passthrough
QSA|qsappend
QSD|qsdiscard
QSL|qslast
R|redirect
S|skip
T|type
UnsafeAllow3F
UnsafePrefixStat
UNCRewriteRule
的行为可以通过一个或多个标志来修改。标志包含在规则末尾的方括号中,
多个标志之间用逗号分隔。
RewriteRule pattern target [Flag1,Flag2,Flag3]
每个标志(除少数例外)都有一个缩写形式,如 CO,
以及一个较长的形式,如 cookie。虽然最常用的是缩写形式,
但建议你熟悉长形式,以便记住每个标志的作用。
某些标志接受一个或多个参数。标志不区分大小写。
修改与请求关联的元数据的标志(T=、H=、E=)在目录级和 htaccess 上下文中,当在同一轮重写处理中执行了替换(非 '-')时不会生效。
下面逐一介绍每个可用的标志,并给出使用示例。
[B] 标志指示 RewriteRule
在应用转换之前转义非字母数字字符。
mod_rewrite 在映射 URL 之前必须对其进行反转义,
因此反向引用在应用时是未转义的。使用 B 标志,反向引用中的非字母数字字符将被转义。
例如,考虑以下规则:
有关类似的服务器变量转义,请参阅 "escape" 映射函数
RewriteRule "^search/(.*)$" "/search.php?term=$1"
给定搜索词 'x & y/z',浏览器会将其编码为
'x%20%26%20y%2Fz',发出请求 'search/x%20%26%20y%2Fz'。不使用 B
标志时,此重写规则将映射为 'search.php?term=x & y/z',
这不是一个有效的 URL,因此会被编码为
search.php?term=x%20&y%2Fz=,这不是预期的结果。
在同一规则上设置 B 标志后,参数会在传递到输出 URL 之前重新编码,
从而正确映射到
/search.php?term=x%20%26%20y%2Fz。
RewriteRule "^search/(.*)$" "/search.php?term=$1" [B,PT]
请注意,你可能还需要将 AllowEncodedSlashes 设置为 On
才能使此特定示例正常工作,因为 httpd 不允许 URL 中包含编码的斜杠,
如果发现编码斜杠则返回 404。
在代理场景中,这种转义尤其必要, 因为后端如果收到未转义的 URL 可能会出错。
此标志的替代方案是使用 RewriteCond 对 %{THE_REQUEST} 进行捕获,
这将以编码形式捕获字符串。
在 2.4.26 及更高版本中,你可以通过列出字符来限制反向引用中要转义的特定字符:
[B=#?;]。注意:空格字符可以包含在要转义的字符列表中,
但你必须引用 RewriteRule
的整个第三个参数,并且空格不能是列表中的最后一个字符。
# 转义空格和问号。最后一个参数周围的引号 # 在包含空格时是必需的。 RewriteRule "^search/(.*)$" "/search.php?term=$1" "[B= ?]"
要限制以这种方式转义的字符,请参阅 #flag_bne 和 #flag_bctls
[BNP] 标志指示 RewriteRule 将反向引用中的空格字符
转义为 %20 而不是 '+'。当反向引用将用于路径部分而不是查询字符串时很有用。
# 在路径中将空格转义为 %20,而不是表单提交中通过 # 查询字符串使用的 + RewriteRule "^search/(.*)$" "/search.php/$1" "[B,BNP]"
此标志在 2.4.26 及更高版本中可用。
[BCTLS] 标志类似于 [B] 标志,但只转义控制字符和空格字符。 这与未编码复制到查询字符串时被拒绝的字符集相同。
# 转义控制字符和空格 RewriteRule "^search/(.*)$" "/search.php/$1" "[BCTLS]"
此标志在 2.5.1 及更高版本中可用。
[BNE=...] 中的字符列表被视为 [B] 或 [BCTLS] 标志的排除项。 列出的字符将不会被转义。
# 转义默认字符,但保留 / RewriteRule "^search/(.*)$" "/search.php?term=$1" "[B,BNE=/]"
此标志在 2.5.1 及更高版本中可用。
[C] 或 [chain] 标志表示该 RewriteRule
与下一条规则链接在一起。也就是说,如果该规则匹配,则照常处理,
控制继续传递到下一条规则。但如果不匹配,
则跳过下一条规则以及所有链接在一起的其他规则。
[CO] 或 [cookie] 标志允许你在特定的
RewriteRule
匹配时设置 cookie。参数由三个必需字段和五个可选字段组成。
该标志的完整语法(包括所有属性)如下:
[CO=NAME:VALUE:DOMAIN:lifetime:path:secure:httponly:samesite]
如果任何 cookie 字段中需要使用字面量 ':' 字符, 可以使用替代语法。要选择替代语法,cookie 的 "Name" 前应加 ';' 字符,字段分隔符也应指定为 ';'。
[CO=;NAME;VALUE:MOREVALUE;DOMAIN;lifetime;path;secure;httponly;samesite]
你必须声明 cookie 的名称、值和域才能设置 cookie。
www.example.com,也可以是域名,
如 .example.com。它必须至少包含两个由点分隔的部分。
也就是说,不能仅为 .com 或 .net。
cookie 安全模型禁止这类 cookie。你还可以选择设置以下值:
/customers/ 或 /files/download/。/——即整个网站。secure、true 或 1,
则 cookie 只允许通过安全(https)连接传输。HttpOnly、true 或
1,cookie 将设置 HttpOnly 标记,
这意味着在支持此功能的浏览器上,JavaScript 代码无法访问该 cookie。false 或 0 以外的任何值,
SameSite 属性将被设置为指定的值。
典型值为 None、Lax 和 Strict。
在 2.5.1 及更高版本中可用。请看以下示例:
RewriteEngine On RewriteRule "^/index\.html" "-" [CO=frontdoor:yes:.example.com:1440:/]
在此示例中,规则不会重写请求。"-" 重写目标告诉
mod_rewrite 不做修改地传递请求。
相反,它设置了一个名为 'frontdoor'、值为 'yes' 的 cookie。
该 cookie 对 .example.com 域中的所有主机有效。
它被设置为在 1440 分钟(24 小时)后过期,并对所有 URI 返回。
DPI 标志使重写后 URI 的 PATH_INFO 部分被丢弃。
此标志在 2.2.12 及更高版本中可用。
在目录级上下文中,每个 RewriteRule
比较的 URI 是 URI 当前值和 PATH_INFO 的拼接。
当前 URI 可以是客户端最初请求的 URI、
mod_rewrite 前一轮处理的结果,
或当前轮 mod_rewrite 处理中先前规则的结果。
相比之下,在每条规则之前附加到 URI 的 PATH_INFO
仅反映本轮 mod_rewrite 处理之前的 PATH_INFO 值。
因此,如果 URI 的大部分内容在多个 RewriteRule
指令中被匹配并复制到替换中,而不考虑 URI 的哪些部分来自当前的
PATH_INFO,则最终 URI 可能会附加多个 PATH_INFO 副本。
在任何替换中使用此标志,如果从此请求先前映射到文件系统所产生的
PATH_INFO 不需要的话。此标志会永久忘记本轮
mod_rewrite 处理开始之前建立的 PATH_INFO。
在当前轮 mod_rewrite 处理完成之前,PATH_INFO
不会被重新计算。此轮处理中的后续规则将只看到替换的直接结果,
而不会附加任何 PATH_INFO。
使用 [E] 或 [env] 标志,你可以设置环境变量的值。 请注意,某些环境变量可能在规则运行后被设置, 从而取消你所设置的值。有关环境变量工作方式的更多详细信息, 请参阅环境变量文档。
此标志的完整语法为:
[E=VAR:VAL] [E=!VAR]
VAL 可以包含会被展开的反向引用($N 或
%N)。
使用缩写形式
[E=VAR]
你可以将名为 VAR 的环境变量设置为空值。
使用以下形式
[E=!VAR]
可以取消设置先前设置的名为 VAR 的环境变量。
环境变量随后可以在多种上下文中使用, 包括 CGI 程序、其他 RewriteRule 指令或 CustomLog 指令。
以下示例在请求的 URI 是图像文件时将名为 'image' 的环境变量设置为 '1'。 然后,该环境变量用于将这些请求从访问日志中排除。
RewriteRule "\.(png|gif|jpg)$" "-" [E=image:1] CustomLog "logs/access_log" combined env=!image
请注意,使用 SetEnvIf
也可以达到相同的效果。此处提供此技术作为示例,而非建议。
使用 [F] 标志会导致服务器向客户端返回 403 Forbidden 状态码。
虽然使用 Deny
指令也可以实现相同的行为,但这种方式在分配 Forbidden 状态时更加灵活。
以下规则将禁止从你的服务器下载 .exe 文件。
RewriteRule "\.exe" "-" [F]
此示例使用 "-" 语法作为重写目标,这意味着请求的 URI 不会被修改。 如果你打算禁止请求,就没有理由将其重写到另一个 URI。
使用 [F] 时,会隐含 [L]——也就是说,响应会立即返回, 不会再评估后续规则。
[G] 标志强制服务器在响应中返回 410 Gone 状态。 这表示资源曾经可用,但现在已不再可用。
与 [F] 标志一样,使用 [G] 标志时通常使用 "-" 语法作为重写目标:
RewriteRule "oldproduct" "-" [G,NC]
使用 [G] 时,会隐含 [L]——也就是说,响应会立即返回, 不会再评估后续规则。
强制使用指定的处理器来处理结果请求。例如, 可以使用此标志强制所有没有文件扩展名的文件由 php 处理器解析:
RewriteRule "!\." "-" [H=application/x-httpd-php]
上面的正则表达式——!\.——将匹配任何不包含字面量
. 字符的请求。
这也可以用于根据某些条件强制使用处理器。例如,
以下代码片段在服务器级上下文中使用时,
允许以 .phps 扩展名请求的 .php
文件由 mod_php 显示其源代码:
RewriteRule "^(/source/.+\.php)s$" "$1" [H=application/x-httpd-php-source]
上面的正则表达式——^(/source/.+\.php)s$——将匹配任何以
/source/ 开头,后跟 1 个或多个字符,
再后跟 .phps 的请求。反向引用
$1 引用正则表达式括号内捕获的匹配内容。
[L] 标志使 mod_rewrite
停止处理规则集。在大多数上下文中,这意味着如果规则匹配,
将不再处理后续规则。这对应于 Perl 中的 last
命令或 C 中的 break 命令。使用此标志表示当前规则应立即应用,
而不考虑后续规则。
如果你在 .htaccess 文件或
<Directory>
配置段中使用 RewriteRule,
理解规则的处理方式非常重要。简化来说,一旦规则处理完毕,
重写后的请求会被交回 URL 解析引擎进行后续处理。在处理重写后的请求时,
可能会再次遇到 .htaccess 文件或
<Directory> 配置段,
从而规则集可能会从头开始重新运行。最常见的情况是某条规则导致了重定向——
无论是内部还是外部重定向——使请求处理重新开始。
因此,如果你在这些上下文中使用
RewriteRule
指令,采取明确措施避免规则循环非常重要,
不要仅依赖 [L] 标志来终止一系列规则的执行,如下所示。
替代标志 [END] 可用于终止当前轮的重写处理, 并阻止在目录级(htaccess)上下文中发生任何后续的重写处理。 这不适用于由外部重定向产生的新请求。
此处给出的示例将把所有请求重写到 index.php,
将原始请求作为查询字符串参数传递给 index.php,
但 RewriteCond
确保如果请求已经是 index.php,
则 RewriteRule 将被跳过。
RewriteBase "/"
RewriteCond "%{REQUEST_URI}" !=/index.php
RewriteRule "^(.*)" "/index.php?req=$1" [L,PT]
[N] 标志使规则集从头开始重新运行,使用到目前为止的规则集结果作为起点。 请极其谨慎地使用,因为它可能导致循环。
例如,如果你想在请求中重复替换某个字符串或字母,可以使用 [Next] 标志。 下面的示例将请求中所有的 A 替换为 B,并持续执行直到没有更多的 A 需要替换。
RewriteRule "(.*)A(.*)" "$1B$2" [N]
你可以将此看作 while 循环:当此模式仍然匹配时
(即 URI 仍然包含 A),执行此替换
(即将 A 替换为 B)。
在 2.5.0 及更高版本中,此模块在 10,000 次迭代后返回错误, 以防止意外循环。可以通过向 N 标志添加参数来指定替代的最大迭代次数。
# 每次循环替换 1 个字符 RewriteRule "(.+)[><;]$" "$1" [N=32000] # ……或者,10 次循环后放弃 RewriteRule "(.+)[><;]$" "$1" [N=10]
使用 [NC] 标志使 RewriteRule
以不区分大小写的方式进行匹配。也就是说,
匹配的 URI 中字母是大写还是小写都无所谓。
在下面的示例中,任何图像文件请求将被代理到你的专用图像服务器。
匹配不区分大小写,因此 .jpg 和 .JPG
文件都可以接受。
RewriteRule "(.*\.(jpg|gif|png))$" "http://images.example.com$1" [P,NC]
默认情况下,当 RewriteRule
导致外部重定向时,输出中不在以下安全集中的字符将被转换为其十六进制编码
(百分比编码)等价物:
A-Z、a-z、
0-9$-_.+!*'(),:;@&=/~例如,# 将被转换为 %23,
? 转换为 %3F。%
字符也会被转义(为 %25),这意味着替换中已有的百分比编码
将被双重编码。
使用 [NE] 标志可防止此转义,允许
# 和 ? 等字符不经修改地传递到重定向 URL。
RewriteRule "^/anchor/(.+)" "/bigpage.html#$1" [NE,R]
上面的示例将 /anchor/xyz 重定向到
/bigpage.html#xyz。省略 [NE] 将导致 #
被转换为其十六进制等价物 %23,
从而导致 404 Not Found 错误。
使用 [NS] 标志可防止规则被用于子请求。例如,
使用 SSI(服务器端包含)包含的页面是子请求,
你可能希望避免在这些子请求上发生重写。此外,
当 mod_dir
尝试查找可能的目录默认文件(如 index.html 文件)的信息时,
这是一个内部子请求,你通常希望避免在此类子请求上进行重写。
在子请求上,应用完整的规则集并不总是有用的,甚至可能导致错误。
使用此标志排除有问题的规则。
要决定是否使用此规则:如果你使用 CGI 脚本为 URL 添加前缀, 强制它们由 CGI 脚本处理,那么在子请求上你很可能会遇到问题 (或显著的性能开销)。在这些情况下,请使用此标志。
作为 HTML 页面一部分加载的图像、JavaScript 文件或 CSS 文件不是子请求—— 浏览器将它们作为单独的 HTTP 请求发出。
使用 [P] 标志使请求由 mod_proxy
处理,并通过代理请求进行处理。例如,
如果你想让所有图像请求由后端图像服务器处理,可以这样做:
RewriteRule "/(.*)\.(jpg|gif|png)$" "http://images.example.com/$1.$2" [P]
使用 [P] 标志隐含 [L]——也就是说,请求会立即通过代理推送, 后续规则将不被考虑。
你必须确保替换字符串是有效的 URI
(通常以 http://hostname 开头),
可以由 mod_proxy 处理。如果不是,你将从代理模块收到错误。
使用此标志可以实现比 ProxyPass
指令更强大的远程内容到本地服务器命名空间的映射。
构造规则的目标 URL 时要小心,考虑允许客户端影响你的服务器将作为代理的 URL 集的安全影响。确保 URL 的方案和主机名部分是固定的, 或者不允许客户端过度影响。
使用此标志会触发 mod_proxy 的使用,
在这种情况下使用默认 worker,不处理持久连接,
因为默认 worker 不处理连接池/重用。
要使用持久连接,你需要至少为目标 URL 的方案和主机部分设置一个
Proxy 块,
其中包含一个 ProxySet
指令,例如设置超时时间。
如果使用 ProxyPass 或
ProxyPassMatch
进行设置,将自动使用持久连接。
注意:必须启用 mod_proxy 才能使用此标志。
RewriteRule 中的目标(或替换字符串)默认被假定为文件路径。
使用 [PT] 标志使其被视为 URI。也就是说,
使用 [PT] 标志会使 RewriteRule 的结果
被传回到 URL 映射中,从而基于位置的映射,如
Alias、
Redirect 或
ScriptAlias
等,有机会生效。
例如,如果你有一个 Alias
指向 /icons,并且有一个 RewriteRule 指向那里,
你应该使用 [PT] 标志以确保
Alias 被正确评估。
Alias "/icons" "/usr/local/apache/icons" RewriteRule "/pics/(.+)\.jpg$" "/icons/$1.gif" [PT]
在这种情况下省略 [PT] 标志会导致 Alias 被忽略, 从而返回"文件未找到"错误。
PT 标志隐含 L 标志:
重写将停止以便将请求传递给下一个处理阶段。
请注意,PT 标志在目录级上下文中是隐含的,
例如 <Directory>
配置段或 .htaccess 文件中。规避此行为的唯一方法是重写为
-。
当替换 URI 包含查询字符串时,
RewriteRule
的默认行为是丢弃现有的查询字符串,并用新生成的替换。
使用 [QSA] 标志可以将查询字符串合并。
请看以下规则:
RewriteRule "/pages/(.+)" "/page.php?page=$1" [QSA]
使用 [QSA] 标志,对 /pages/123?one=two 的请求将被映射到
/page.php?page=123&one=two。不使用 [QSA]
标志时,同一请求将被映射到
/page.php?page=123——也就是说,现有的查询字符串将被丢弃。
当请求的 URI 包含查询字符串而目标 URI 不包含时,
RewriteRule
的默认行为是将该查询字符串复制到目标 URI。
使用 [QSD] 标志可以丢弃查询字符串。
此标志在 2.4.0 及更高版本中可用。
同时使用 [QSD] 和 [QSA] 将导致 [QSD] 优先。
如果目标 URI 有查询字符串,将执行默认行为——
也就是说,原始查询字符串将被丢弃并替换为 RewriteRule
目标 URI 中的查询字符串。
默认情况下,替换中第一个(最左边的)问号将路径与查询字符串分开。
使用 [QSL] 标志指示
RewriteRule
改为使用最后一个(最右边的)问号来分隔两个组件。
当映射到文件名中包含字面量问号的文件时,这很有用。 如果替换中未使用查询字符串,可以结合此标志在替换后附加一个问号。
此标志在 2.4.19 及更高版本中可用。
使用 [R] 标志会导致向浏览器发出 HTTP 重定向。
如果指定了完全限定的 URL(即包含 http://servername/),
则将重定向到该位置。否则,将使用当前协议、服务器名称和端口号
来生成与重定向一起发送的 URL。
可以使用 [R=305] 语法指定任何有效的 HTTP 响应状态码,
如果未指定则默认使用 302 状态码。指定的状态码不一定必须是重定向(3xx)状态码。
但是,如果状态码超出重定向范围(300-399),替换字符串将被完全丢弃,
重写将像使用了 L 一样停止。
除了响应状态码外,你还可以使用它们的符号名称指定重定向状态:
temp(默认)、permanent 或 seeother。
你几乎总是希望将 [R] 与 [L] 结合使用(即使用 [R,L]),
因为 [R] 标志本身只是在 URI 前面添加
http://thishost[:thisport],然后将其传递给规则集中的下一条规则,
这通常会导致"请求中的 URI 无效"警告。
注意:httpd 仅支持 HTTP 规范中包含的状态码。 使用无法识别的状态码将导致 500 错误和错误日志消息。
[S] 标志用于跳过你不想运行的规则。
跳过标志的语法为 [S=N],其中 N
表示要跳过的规则数(前提是
RewriteRule 及其前面的
RewriteCond
指令匹配)。这可以看作是重写规则集中的 goto 语句。
在以下示例中,我们只想在请求的 URI
不对应实际文件时才运行 RewriteRule。
# Is the request for a non-existent file?
RewriteCond "%{REQUEST_FILENAME}" !-f
RewriteCond "%{REQUEST_FILENAME}" !-d
# If so, skip these two RewriteRules
RewriteRule ".?" "-" [S=2]
RewriteRule "(.*\.gif)" "images.php?$1"
RewriteRule "(.*\.html)" "docs.php?$1"
此技术很有用,因为 RewriteCond 只应用于紧随其后的
RewriteRule。
因此,如果你想让一个 RewriteCond 应用于多个
RewriteRule,一种可能的技术是否定这些条件并添加一个带有
[Skip] 标志的 RewriteRule。你可以用此构造伪
if-then-else 结构:then 子句的最后一条规则变为
skip=N,其中 N 是 else 子句中的规则数:
# Does the file exist?
RewriteCond "%{REQUEST_FILENAME}" !-f
RewriteCond "%{REQUEST_FILENAME}" !-d
# Create an if-then-else construct by skipping 3 lines if we meant to go to the "else" stanza.
RewriteRule ".?" "-" [S=3]
# IF the file exists, then:
RewriteRule "(.*\.gif)" "images.php?$1"
RewriteRule "(.*\.html)" "docs.php?$1"
# Skip past the "else" stanza.
RewriteRule ".?" "-" [S=1]
# ELSE...
RewriteRule "(.*)" "404.php?file=$1"
# END
使用 <If>、<ElseIf> 和 <Else> 指令可能更容易实现这种配置。
设置响应将以何种 MIME 类型发送。这与 AddType 指令的效果相同。
例如,你可以使用以下技术在以特定方式请求时将 Perl 源代码作为纯文本提供:
# 将 .pl 文件作为纯文本提供 RewriteRule "\.pl$" "-" [T=text/plain]
或者,如果你有一台相机生成没有文件扩展名的 jpeg 图像, 你可以根据文件名强制使用正确的 MIME 类型来提供这些图像:
# 文件名中包含 'IMG' 的是 jpg 图像。 RewriteRule "IMG" "-" [T=image/jpg]
请注意,这是一个简单的示例,使用
<FilesMatch>
可以更好地完成。在使用重写之前,始终考虑问题的替代解决方案,
因为重写总是比替代方案效率更低。
如果在目录级上下文中使用,在整轮 mod_rewrite
处理过程中仅使用 -(破折号)作为替换,
否则使用此标志设置的 MIME 类型将由于内部重新处理
(包括后续轮次的 mod_rewrite 处理)而丢失。
L 标志在此上下文中可用于结束当前轮的
mod_rewrite 处理。
如果被重写的 HTTP 请求包含编码的问号 '%3f', 且重写结果的替换中包含 '?',则需要设置此标志才能允许重写继续。 这可以防止恶意 URL 利用捕获和重新替换编码的问号。
当服务器范围内的替换以变量或反向引用开头并解析为文件系统路径时, 需要设置此标志。这些替换不会以文档根目录为前缀。 这可以防止恶意 URL 导致展开的替换映射到意外的文件系统位置。
Available in Apache HTTP Server 2.5.1 and later.