首页 > 解决方案 > 当模式为 ^(.*)$ 时,为什么 $1 不存储完整的 URL?

问题描述

已经有几个关于这个的话题。但是我还没有找到答案,或者我仍然没有正确理解它。
我知道这$1代表了 RewriteRule 正则表达式中第一组括号的匹配。$1也存储这个值。
但如果只有^(.*)$,那么它的工作方式似乎有所不同?

示例:
网址:http://www.example.com/

RewriteBase /
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC,OR]
RewriteCond %{HTTPS_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]

我的理解:
1.http://www.example.com/匹配RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]并将匹配存储在%1(=example.com/)中。
2. 转到 RewriteRule,因为在步骤 1 中匹配的 URL
3. RewriteRule 获取字符串http://www.example.com/。因为^(.*)$,http://www.example.com/完全匹配并存储在$1.
4. 我认为这个 URL 应该出现:https://example.com/http://www.example.com/

实际出现的内容:https://example.com/

为什么$1会有一个空字符串?这一切都匹配,不是吗?

标签: apache.htaccessredirectmod-rewrite

解决方案


这里有很多误解,我将尝试解决...

RewriteBase /
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC,OR]
RewriteCond %{HTTPS_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]

我将忽略RewriteBase指令和第二个RewriteCond指令......

RewriteBase指令不适用于此处,因为没有相对路径替换字符串(该RewriteRule指令的第二个参数)。

没有HTTPS_HOST服务器变量,只有HTTP_HOST. 请参阅有关 ServerFault 的以下问题:https ://serverfault.com/questions/953020/what-is-the-difference-between-http-host-and-https-host-in-apache-htaccess-file

我认为HTTPS_HOST由于一些被盲目复制/粘贴的拼写错误/误解而在互联网上长期存在。

HTTP_HOST包含HostHTTP 请求标头(主机名)的值,例如。www.example.com或者example.com,取决于请求的内容。因此名称HTTP_+ HOST。这是用于所有 HTTP 请求标头的相同命名约定。为每个创建相应的服务器变量。

因此,这变为(OR从第一个条件中删除标志):

RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]

RewriteRule 模式(例如^(.*)$

但如果只有^(.*)$,那么它的工作方式似乎有所不同?

不,它的工作原理相同。混乱似乎是该RewriteRule 模式实际匹配的内容。

RewriteRule 模式仅与 URL 路径匹配。

URL 路径是在方案 + 主机名之后和查询字符串之前的 URL 部分。例如。给定一个请求,http://example.com/那么 URL-path 就是简单的/. 或请求http://example.com/foo/bar?param=1- URL 路径是/foo/bar.

但是,在每个目录的上下文中.htaccess(与服务器虚拟主机上下文相反),在匹配发生之前首先从 URL 路径中删除目录前缀。(因为在请求映射到文件系统并且严格来说与文件路径匹配之后进行处理。)目录前缀是文件本身的绝对文件路径,特别是以斜杠结尾。例如。当文件位于文档根目录中时,目录前缀将类似于(文档根目录的文件系统路径)。.htaccess.htaccess.htaccess/var/www/user/public_html/

所以,给定一个请求,那么与模式匹配的http://example.com/URL 路径就是“”(空字符串)。或者请求- 匹配的 URL 路径是- 没有斜杠前缀。RewriteRule .htaccesshttp://example.com/foo/bar?param=1foo/bar

.htaccess当文件位于文档根目录之外的子目录中时,这一点更为重要。例如,如果.htaccess文件位于/subdir子目录中并且有表单的请求,则http://example.com/subdir/foo/barRewriteRule 模式将再次与 just foo/bar(not subdir/foo/baror /subdir/foo/bar) 匹配。这与在服务器(或虚拟主机)上下文RewriteRule中使用指令时存在显着差异。在服务器上下文中,该模式始终与完整的 URL 路径匹配,以斜杠开头 - 在服务器上下文中使用时没有目录前缀的概念,因为指令是在之前处理的RewriteRule 请求被映射到文件系统。

我的理解:

  1. http://www.example.com/匹配RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]并将匹配存储在%1(=example.com/) 中。
  2. 转到RewriteRule因为在步骤 1 中匹配的 URL
  3. RewriteRule 获取字符串http://www.example.com/。因为^(.*)$,http://www.example.com/完全匹配并存储在$1.
  4. 我认为这个 URL 应该出现:https://example.com/http://www.example.com/

您的处理顺序错误。它实际上是首先处理的RewriteRule 模式。仅当RewriteRule 模式匹配时才处理前面的RewriteCond条件)。如果所有条件都成功,则发生RewriteRule 替换(第二个参数)。

因此,按顺序,给定一个请求http://www.example.com/

  1. RewriteRule ^(.*)$- 生成的 URL 路径“”(空字符串)与RewriteRule 模式 ^(.*)$匹配。然后$1反向引用保存一个空字符串($0反向引用也是如此 - 它存储整个模式的匹配 - 在这种情况下相同)
  2. RewriteCond %{HTTP_HOST} ^www\.(.*)$- 如果RewriteRule 模式在步骤#1 中匹配(在这种情况下匹配),则RewriteCond处理前面的指令。这与Host标题匹配,例如。www.example.com(否http://)反对正则表达式^www\.(.*)$。如果这成功,则%1反向引用保存第一个捕获组的值,即。example.com在这个例子中。
  3. RewriteRule ^(.*)$ https://%1/$1 [R=301,L]- 如果前面的条件成功,则指令中的替换(即https://%1/$1)发生。RewriteRuleIE。https://example.com/-来自最后一个匹配的CondPattern%1中的捕获组,并且是一个空字符串,来自模式中的捕获组。example.om$1RewriteRule

其他注意事项:

  • 由于处理的顺序,在RewriteRule 模式中尽可能多地进行模式匹配,而不是依赖于前面的RewriteCond指令,自然会更有效率。(一个常见的误解是RewriteCond指令首先被处理——事实并非如此。)
  • 由于处理顺序,您可以在前面指令的TestString$n (第一个)参数中使用反向引用。(如果指令是自上而下处理的,这是不可能的。)RewriteCond

  • %n反向引用仅来自最后匹配CondPattern如果您有多种情况,请务必考虑这一点。


推荐阅读