Apache HTTP 服务器版本 2.4
一个 RewriteRule
可以通过一个或多个标志来修改其行为。标志包含在规则末尾的方括号中,多个标志用逗号分隔。
RewriteRule pattern target [Flag1,Flag2,Flag3]
每个标志(除少数例外)都有一个简短形式,例如 CO
,以及一个较长形式,例如 cookie
。虽然最常见的是使用简短形式,但建议您熟悉长形式,以便您记住每个标志应该做什么。一些标志接受一个或多个参数。标志不区分大小写。
在每个目录和 htaccess 上下文中,当在同一轮重写处理期间执行替换(除“-”以外)时,更改与请求关联的元数据的标志(T=、H=、E=)不会产生任何影响。
这里介绍了每个可用标志,以及如何使用它们的示例。
[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
的整个第三个参数,并且空格不能是列表中的最后一个字符。
# Escape spaces and question marks. The quotes around the final argument # are required when a space is included. RewriteRule "^search/(.*)$" "/search.php?term=$1" "[B= ?]"
要限制以这种方式转义的字符,请参见 #flag_bne 和 #flag_bctls
[BNP] 标志指示 RewriteRule
将反向引用中的空格字符转义为 %20 而不是 '+'. 当反向引用将用于路径组件而不是查询字符串时,这很有用。
# Escape spaces to %20 in the path instead of + as used in form submission via # the query string RewriteRule "^search/(.*)$" "/search.php/$1" "[B,BNP]"
此标志在版本 2.4.26 及更高版本中可用。
[BCTLS] 标志类似于 [B] 标志,但只转义控制字符和空格字符。这与在未编码的情况下复制到查询字符串时被拒绝的字符集相同。
# Escape control characters and spaces RewriteRule "^search/(.*)$" "/search.php/$1" "[BCTLS]"
此标志在版本 2.4.57 及更高版本中可用。
[BNE=...] 中的字符列表被视为 [B] 或 [BCTLS] 标志字符的排除项。列出的字符不会被转义。
# Escape the default characters, but leave / RewriteRule "^search/(.*)$" "/search.php?term=$1" "[B,BNE=/]"
此标志在版本 2.4.57 及更高版本中可用。
[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 的名称、值和域。
www.example.com
,也可以是域,例如 .example.com
。它必须至少包含两个用点分隔的部分。也就是说,它不能仅仅是 .com
或 .net
。这种类型的 cookie 被 cookie 安全模型禁止。您也可以选择设置以下值
/customers/
或 /files/download/
。/
- 也就是说,整个网站。secure
、true
或 1
,则 cookie 只能通过安全 (https) 连接进行传输。HttpOnly
、true
或 1
,则 cookie 将设置 HttpOnly
标志,这意味着 cookie 对支持此功能的浏览器的 JavaScript 代码不可访问。false
或 0
以外的任何值,则 SameSite
属性将设置为指定的值。典型值是 None
、Lax
和 Strict
。在 2.4.47 及更高版本中可用。请考虑以下示例
RewriteEngine On RewriteRule "^/index\.html" "-" [CO=frontdoor:yes:.example.com:1440:/]
在给出的示例中,规则不会重写请求。“-”重写目标告诉 mod_rewrite 不更改请求。相反,它将名为“frontdoor”的 cookie 设置为“yes”。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。PATH_INFO 不会在当前轮 mod_rewrite 处理完成之前重新计算。此轮处理期间的后续规则只会看到替换的直接结果,而不会附加任何 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]
上面的正则表达式 - !\.
- 将匹配任何不包含字面量 .
字符的请求。
这也可以用来根据某些条件强制处理程序。例如,以下在每个服务器上下文中使用的代码片段允许 .php
文件在使用 .phps
扩展名请求时由 mod_php
显示
RewriteRule "^(/source/.+\.php)s$" "$1" [H=application/x-httpd-php-source]
上面的正则表达式 - ^(/source/.+\.php)s$
- 将匹配任何以 /source/
开头,后面跟着 1 个或 n 个字符,最后以 .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.4.8 及更高版本中,此模块在 10,000 次迭代后返回错误,以防止意外循环。可以通过向 N 标志添加内容来指定其他最大迭代次数。
# Be willing to replace 1 character in each pass of the loop RewriteRule "(.+)[><;]$" "$1" [N=32000] # ... or, give up if after 10 loops RewriteRule "(.+)[><;]$" "$1" [N=10]
使用 [NC] 标志会导致 RewriteRule
以不区分大小写的方式进行匹配。也就是说,它不关心匹配的 URI 中的字母是大写还是小写。
在下面的示例中,对任何图像文件的请求都将被代理到您的专用图像服务器。匹配不区分大小写,因此例如 .jpg
和 .JPG
文件都是可以接受的。
RewriteRule "(.*\.(jpg|gif|png))$" "http://images.example.com$1" [P,NC]
默认情况下,特殊字符(例如 &
和 ?
)将被转换为其十六进制代码等效项,用于导致外部重定向的规则。使用 [NE] 标志可以防止这种情况发生。
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
的使用,而不会处理持久连接,因为在这种情况下使用默认工作程序,它不处理连接池/重用。
为了使用持久连接,您需要至少为目标 URL 的方案和主机部分设置一个 Proxy
块,其中包含一个 ProxySet
指令,您可以在其中设置超时等。
如果您使用 ProxyPass
或 ProxyPassMatch
设置它,则会自动使用持久连接。
注意:必须启用 mod_proxy
才能使用此标志。
默认情况下,RewriteRule
中的目标(或替换字符串)被认为是文件路径。使用 [PT] 标志会导致它被视为 URI。也就是说,使用 [PT] 标志会导致 RewriteRule
的结果被重新传递到 URL 映射,以便基于位置的映射,例如 Alias
、Redirect
或 ScriptAlias
等,有机会生效。
例如,如果您有一个指向 /icons 的 Alias
,并且有一个指向那里的 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] 标志本身会将 http://thishost[:thisport]
添加到 URI 的前面,但随后会将其传递到规则集中的下一条规则,这通常会导致“请求中的 URI 无效”警告。
[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-clause 的最后一条规则变为 skip=N
,其中 N 是 else-clause 中规则的数量
# 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 源代码,如果以特定方式请求。
# Serve .pl files as plain text RewriteRule "\.pl$" "-" [T=text/plain]
或者,也许,如果您有一台产生没有文件扩展名的 jpeg 图像的相机,您可以通过其文件名强制这些图像以正确的 MIME 类型提供服务。
# Files with 'IMG' in the name are jpg images. RewriteRule "IMG" "-" [T=image/jpg]
请注意,这是一个简单的示例,可以使用 <FilesMatch>
来更好地完成。在诉诸重写之前,始终考虑问题的替代解决方案,重写将始终比替代方案效率低。
如果在每个目录上下文中使用,则仅使用 -
(破折号)作为整个 mod_rewrite 处理轮次的替换,否则使用此标志设置的 MIME 类型将由于内部重新处理(包括随后的 mod_rewrite 处理轮次)而丢失。L
标志在此上下文中很有用,可以结束当前轮次的 mod_rewrite 处理。
如果要重写的 HTTP 请求具有编码的问号“%3f”,并且重写后的结果在替换中具有“?”,则需要设置此标志以允许重写继续。这可以防止恶意 URL 利用编码问号的捕获和重新替换。
如果服务器范围内的替换以变量或反向引用开头并解析为文件系统路径,则需要设置此标志。这些替换没有以文档根目录为前缀。这可以防止恶意 URL 导致扩展的替换映射到意外的文件系统位置。