我在使用 Apache 的 RewriteCond 和 RewriteRule 条件时遇到了困难。我通常不经常使用它们,而且大多数文档都提供了足够的信息让我自己找到解决方案。但这次我遇到了如何组合多个重写情况的难题。
在 Apache 2.4.17 上,我有一个虚拟主机,其中多个 ServerAliases 都指向相同的 ServerName 和目录。每个配置的 ServerAlias 都应重定向到 ServerName 域。因此,当我访问 sub1.domain.com、sub2.domain.com 或 sub3.domain.com 时,它们都应通过 HTTP 301 重定向到 www.domain.com。
我还想将所有 URL 重定向到同一个 PHP 输入文件。例如:www.domain.com/news/1 应在内部重定向到虚拟主机的 documentroot 处的 index.php 文件,而无需更改用户 Web 浏览器中的 URL。
上述情况确实可以独立起作用,但是当我尝试在 .htaccess 文件中结合这些情况时,事情就出错了。
我的域名重定向示例:
AcceptPathInfo On
Options +MultiViews
Options +FollowSymLinks
RewriteEngine On
# Following solution inspired by https://makandracards.com/makandra/922-apache-redirect-all-requests-from-one-host-to-another
RewriteCond %{HTTP_HOST} !^www.domain.com$
RewriteRule ^(.*)$ "https://www.domain.com/$1" [NC,QSA,R=301]
效果很好。
这是我通常使用的代码,将所有 URL 内部重定向到 documentroot 处的 index.php,以便能够根据 URL 生成动态内容:
AcceptPathInfo On
Options +MultiViews
Options +FollowSymLinks
RewriteEngine On
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteRule ^.*$ ./index.php
这也很好用。请求不会在用户浏览器上转发到 index.php,而只会在内部转发。
当我尝试结合两个重定向时:
AcceptPathInfo On
Options +MultiViews
Options +FollowSymLinks
RewriteEngine On
# Following solution inspired by https://makandracards.com/makandra/922-apache-redirect-all-requests-from-one-host-to-another
RewriteCond %{HTTP_HOST} !^www.domain.com$
RewriteRule ^(.*)$ "https://www.domain.com/$1" [NC,QSA,R=301]
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteRule ^.*$ ./index.php
然后事情就出错了。对其他域(例如 sub1.domain.com、sub2.domain.com/test 等)的所有请求都会导致 Apache 错误:
永久移动
该文件已移至此处。
此外,尝试使用 ErrorDocument 处理请求时遇到 301 永久移动错误。
而 www.domain.com/test 正确地提供了文档根目录下 index.php 的内容。
当我尝试改变条件和规则的顺序时:
AcceptPathInfo On
Options +MultiViews
Options +FollowSymLinks
RewriteEngine On
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteRule ^.*$ ./index.php
# Following solution inspired by https://makandracards.com/makandra/922-apache-redirect-all-requests-from-one-host-to-another
RewriteCond %{HTTP_HOST} !^www.domain.com$
RewriteRule ^(.*)$ "https://www.domain.com/$1" [NC,QSA,R=301]
然后,sub1.domain.com 在用户浏览器中被正确重定向到 www.domain.com,但是当我向它附加一个 URI(如 sub1.domain.com/test)时,它会被重定向到 www.domain.com/index.php 而不是 www.domain.com/test,并且仅在文档根目录内部获取 index.php 的内容。
在这种情况下直接请求 www.domain.com/test 确实有效:显示文档根目录下的 index.php 内容,而不是将用户重定向到 index.php。
我相信这和一些标志有关,但不知道该如何应用它们。谁能告诉我要注意什么?
答案1
我认为我已经通过[L]
在两RewriteRule
行中添加标志来解决这个问题。
这是我现在的 htaccess 文件:
AcceptPathInfo On
Options +MultiViews
Options +FollowSymLinks
RewriteEngine On
# Following solution inspired by https://makandracards.com/makandra/922-apache-redirect-all-requests-from-one-host-to-another
RewriteCond %{HTTP_HOST} !^www.domain.com$
RewriteRule ^(.*)$ "https://www.domain.com/$1" [NC,QSA,R=301,L]
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteRule ^.*$ ./index.php [L]
据我了解,Apache 现在将首先考虑
RewriteCond %{HTTP_HOST} !^www.domain.com$
然后执行
RewriteRule ^(.*)$ "https://www.domain.com/$1" [NC,QSA,R=301,L]
由于RewriteRule
已设置标志[L]
,Apache 将不会检查任何其他规则。
当完整的 URL 不符合要求时,第一个RewriteCond
Apache 将跳过该RewriteCond
/RewriteRule
组合并转到带有
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteRule ^.*$ ./index.php [L]
仅限内部重定向。现在它不会与第一个合并,RewriteRule
因为RewriteRule
它的[L]
标志会被跳过。
我说得对吗?有人能确认或否认这个解释吗?或者可以澄清一下吗?