^ 字符不工作 mod 在 htaccess 中重写
^ character not working on mod rewrite in htaccess
我在 .htaccess
文件中的重写规则遇到了这个非常烦人的问题。
上下文
所以我想要的是将这两种类型的 URL 重写到不同的目标:
URL 1 -- http://example.com/rem/call/answer/{Hex String}/{Hex String}/
URL 2 -- http://example.com/answer/{Hex String}/{Hex String}/
这是我的 .htaccess 文件的摘录:
RewriteEngine On
RewriteRule rem/call/answer/([a-f0-9]+)/([a-f0-9]+)/?$ /TARGET1
RewriteRule answer/([a-f0-9]+)/([a-f0-9]+)/?$ /TARGET2
问题
现在的问题是 URL 2 重写得很好(使用 规则 #2)并转到 TARGET 2,但是 URL 1 重写了两条规则,而不仅仅是 rule #1.
我尝试了几种解决方案,包括明显使用字符 ^
作为 “字符串开头”。那时,我的重写规则是:
RewriteEngine On
RewriteRule rem/call/answer/([a-f0-9]+)/([a-f0-9]+)/?$ /TARGET1
RewriteRule ^answer/([a-f0-9]+)/([a-f0-9]+)/?$ /TARGET2
然而,另一个问题发生了。这次是 URL 1 重写得很好,只有 rule #1 并转到 TARGET 1。但是现在 URL 2 根本不再重写了。我猜这是因为第二个重写规则从不匹配任何 url 因此永远不会应用。
到目前为止我找到的唯一解决方案是删除 ^
并在 规则 #1 末尾使用 [L]
标志,如下所示:
RewriteEngine On
RewriteRule rem/call/answer/([a-f0-9]+)/([a-f0-9]+)/?$ /TARGET1 [L]
RewriteRule answer/([a-f0-9]+)/([a-f0-9]+)/?$ /TARGET2
这样,它使用规则 #1,匹配,但永远不会达到规则 #2。两个 urls 都用这些规则正确重写了,但这不是一个好的解决方案,因为我可能不想在第一个之后停止 URL 1 的重写规则适用(如果我有第三条规则我也想应用它...)
我想问你的问题
既然我已经陈述了问题,我的问题是:
[L]
标志是唯一的出路吗? (我非常怀疑,当然希望不会)
^
会是一个候选解决方案吗? (我也这么认为)
- 如果是这样,如何让它工作以及为什么在我的情况下它根本不起作用?
我怀疑的是
我怀疑这与 URL 实际上是 http://example.com/answer/{Hex String}/{Hex String}/
而不仅仅是 answer/{Hex String}/{Hex String}/
这一事实有关,这意味着 answer/..
不是确实在字符串的开头,因此用 ^
作为前缀是行不通的。
但这又让我想到了另一个问题:
如何告诉 apache 去掉 scheme+domain 部分的 url(即 http://example.com/
)并将规则与 url 的其余部分匹配(例如 answer/{Hex String}/{Hex String}/
) ?
编辑
我还应该补充一点,我已经尝试过基本的 alice-bob 示例。我的根目录中有一个名为 bob.html 的文件,.htaccess
文件中有以下规则:
RewriteRule alice.html$ /bob.html
这很好用,并在查询 alice.html 时显示 bob.html 页面。但是,如果我将规则更改为:
RewriteRule ^alice.html$ /bob.html
然后我在查询 alice.html 页面时收到 404 错误...
至于@anubhava的评论,我完整的.htaccess文件组成如下:
RewriteEngine On
[A bunch of RewriteRule that have nothing to do with the topic at hand
(don't contain any "answer" string in them and all work perfectly)]
RewriteRule rem/call/answer/([a-f0-9]+)/([a-f0-9]+)/?$ /TARGET1 [L]
RewriteRule answer/([a-f0-9]+)/([a-f0-9]+)/?$ /TARGET2
ErrorDocument 404 /404.html
Header set Access-Control-Allow-Origin "*"
SetEnv file_uploads On
好的,感谢@anubhava 的评论,我通过将 .htaccess
文件向下移动一级到 www
目录轻松解决了这个问题。
我还是很好奇为什么这解决了我的问题,所以我继续研究apache的重写是如何工作的。我不确定我是否了解了所有细节,但这是我发现的。
位置位置位置
当然,文件的位置当然很重要,尤其是像.htaccess
这样的配置文件。但它甚至超越了简单的文件路径,原因如下:
首先,您需要记住 .htaccess
文件会影响它所在的目录及其所有子目录。因此,将全局 .htaccess
文件放在您网站的根目录下似乎是合乎逻辑的,因为它会影响所有子目录(即整个网站)。
要记住的第二件事是 public_html
目录(在我的例子中称为 www
,只是一个符号 link 到 public_html
) 是您网站内容的根文件夹。您可能可以访问它的父目录,但是您放在 public_html
目录之外的任何内容本身都不是您网站内容的一部分,您放在那里的任何资源都不会成为您网站层次结构的一部分(即不可访问通过 http://example.com/path/to/resource
).
正则表达式选项 ^
匹配字符串的开头,这里在 URL 重写的上下文中,它是所考虑的 URL 的开头。这还不是全部,Apache 似乎会根据 .htaccess
文件的位置来解析匹配。这意味着 ^
不仅引用您作为规则的一部分编写的字符串的开头,而且实际上引用它相对于充当 "local root directory" 的 .htaccess
文件的实际路径对于该特定 .htaccess
文件中的所有重写规则。
例子
假设您有一个子目录(例如 http://example.com/sub/directory/
)并且其中有两个文件:
http://example.com/sub/directory/.htaccess
http://example.com/sub/directory/bob.html
在此 .htaccess 文件中,您有如下重写规则:
RewriteRule ^tom.html$ /sub/directory/bob.html
此规则不会匹配 http://example.com/tom.html
,正如您预期的那样 ^
会起作用,而是会匹配 http://example.com/sub/directory/tom.html
,因为这是 .htaccess
文件所在的位置.
结论
一般来说,假设您有一个重写规则,例如:
RewriteRule ^PATH$ /TARGET_PATH
这意味着该规则不会将 URL 与 ^PATH$
匹配,而是实际上将其与 ^[Location of the .htaccess file]/PATH$
匹配
换句话说,.htaccess
文件的位置充当其中所有重写规则的一种基础 URL(与 html 中的基础标记非常相似) .
这就是为什么我的 ^
重写规则不起作用的原因,因为我的 .htaccess
文件位于 public_html
目录之上,并且该父目录充当基础目录URL 我的规则。因此,该规则永远不会匹配任何 URL,因为它将它与从未访问过的路径进行比较(因为在网站的内容根目录之上)。
我希望这足够清楚,以帮助可能遇到我遇到的相同问题的任何人。
干杯
我在 .htaccess
文件中的重写规则遇到了这个非常烦人的问题。
上下文
所以我想要的是将这两种类型的 URL 重写到不同的目标:
URL 1 -- http://example.com/rem/call/answer/{Hex String}/{Hex String}/
URL 2 -- http://example.com/answer/{Hex String}/{Hex String}/
这是我的 .htaccess 文件的摘录:
RewriteEngine On
RewriteRule rem/call/answer/([a-f0-9]+)/([a-f0-9]+)/?$ /TARGET1
RewriteRule answer/([a-f0-9]+)/([a-f0-9]+)/?$ /TARGET2
问题
现在的问题是 URL 2 重写得很好(使用 规则 #2)并转到 TARGET 2,但是 URL 1 重写了两条规则,而不仅仅是 rule #1.
我尝试了几种解决方案,包括明显使用字符 ^
作为 “字符串开头”。那时,我的重写规则是:
RewriteEngine On
RewriteRule rem/call/answer/([a-f0-9]+)/([a-f0-9]+)/?$ /TARGET1
RewriteRule ^answer/([a-f0-9]+)/([a-f0-9]+)/?$ /TARGET2
然而,另一个问题发生了。这次是 URL 1 重写得很好,只有 rule #1 并转到 TARGET 1。但是现在 URL 2 根本不再重写了。我猜这是因为第二个重写规则从不匹配任何 url 因此永远不会应用。
到目前为止我找到的唯一解决方案是删除 ^
并在 规则 #1 末尾使用 [L]
标志,如下所示:
RewriteEngine On
RewriteRule rem/call/answer/([a-f0-9]+)/([a-f0-9]+)/?$ /TARGET1 [L]
RewriteRule answer/([a-f0-9]+)/([a-f0-9]+)/?$ /TARGET2
这样,它使用规则 #1,匹配,但永远不会达到规则 #2。两个 urls 都用这些规则正确重写了,但这不是一个好的解决方案,因为我可能不想在第一个之后停止 URL 1 的重写规则适用(如果我有第三条规则我也想应用它...)
我想问你的问题
既然我已经陈述了问题,我的问题是:
[L]
标志是唯一的出路吗? (我非常怀疑,当然希望不会)^
会是一个候选解决方案吗? (我也这么认为)- 如果是这样,如何让它工作以及为什么在我的情况下它根本不起作用?
我怀疑的是
我怀疑这与 URL 实际上是 http://example.com/answer/{Hex String}/{Hex String}/
而不仅仅是 answer/{Hex String}/{Hex String}/
这一事实有关,这意味着 answer/..
不是确实在字符串的开头,因此用 ^
作为前缀是行不通的。
但这又让我想到了另一个问题:
如何告诉 apache 去掉 scheme+domain 部分的 url(即 http://example.com/
)并将规则与 url 的其余部分匹配(例如 answer/{Hex String}/{Hex String}/
) ?
编辑
我还应该补充一点,我已经尝试过基本的 alice-bob 示例。我的根目录中有一个名为 bob.html 的文件,.htaccess
文件中有以下规则:
RewriteRule alice.html$ /bob.html
这很好用,并在查询 alice.html 时显示 bob.html 页面。但是,如果我将规则更改为:
RewriteRule ^alice.html$ /bob.html
然后我在查询 alice.html 页面时收到 404 错误...
至于@anubhava的评论,我完整的.htaccess文件组成如下:
RewriteEngine On
[A bunch of RewriteRule that have nothing to do with the topic at hand
(don't contain any "answer" string in them and all work perfectly)]
RewriteRule rem/call/answer/([a-f0-9]+)/([a-f0-9]+)/?$ /TARGET1 [L]
RewriteRule answer/([a-f0-9]+)/([a-f0-9]+)/?$ /TARGET2
ErrorDocument 404 /404.html
Header set Access-Control-Allow-Origin "*"
SetEnv file_uploads On
好的,感谢@anubhava 的评论,我通过将 .htaccess
文件向下移动一级到 www
目录轻松解决了这个问题。
我还是很好奇为什么这解决了我的问题,所以我继续研究apache的重写是如何工作的。我不确定我是否了解了所有细节,但这是我发现的。
位置位置位置
当然,文件的位置当然很重要,尤其是像.htaccess
这样的配置文件。但它甚至超越了简单的文件路径,原因如下:
首先,您需要记住
.htaccess
文件会影响它所在的目录及其所有子目录。因此,将全局.htaccess
文件放在您网站的根目录下似乎是合乎逻辑的,因为它会影响所有子目录(即整个网站)。要记住的第二件事是
public_html
目录(在我的例子中称为www
,只是一个符号 link 到public_html
) 是您网站内容的根文件夹。您可能可以访问它的父目录,但是您放在public_html
目录之外的任何内容本身都不是您网站内容的一部分,您放在那里的任何资源都不会成为您网站层次结构的一部分(即不可访问通过http://example.com/path/to/resource
).正则表达式选项
^
匹配字符串的开头,这里在 URL 重写的上下文中,它是所考虑的 URL 的开头。这还不是全部,Apache 似乎会根据.htaccess
文件的位置来解析匹配。这意味着^
不仅引用您作为规则的一部分编写的字符串的开头,而且实际上引用它相对于充当 "local root directory" 的.htaccess
文件的实际路径对于该特定.htaccess
文件中的所有重写规则。
例子
假设您有一个子目录(例如 http://example.com/sub/directory/
)并且其中有两个文件:
http://example.com/sub/directory/.htaccess
http://example.com/sub/directory/bob.html
在此 .htaccess 文件中,您有如下重写规则:
RewriteRule ^tom.html$ /sub/directory/bob.html
此规则不会匹配 http://example.com/tom.html
,正如您预期的那样 ^
会起作用,而是会匹配 http://example.com/sub/directory/tom.html
,因为这是 .htaccess
文件所在的位置.
结论
一般来说,假设您有一个重写规则,例如:
RewriteRule ^PATH$ /TARGET_PATH
这意味着该规则不会将 URL 与 ^PATH$
匹配,而是实际上将其与 ^[Location of the .htaccess file]/PATH$
换句话说,.htaccess
文件的位置充当其中所有重写规则的一种基础 URL(与 html 中的基础标记非常相似) .
这就是为什么我的 ^
重写规则不起作用的原因,因为我的 .htaccess
文件位于 public_html
目录之上,并且该父目录充当基础目录URL 我的规则。因此,该规则永远不会匹配任何 URL,因为它将它与从未访问过的路径进行比较(因为在网站的内容根目录之上)。
我希望这足够清楚,以帮助可能遇到我遇到的相同问题的任何人。
干杯