首页 > 解决方案 > $_GET["foo"] 来自 RewriteRule 的 NULL

问题描述

我正在尝试将http://localhost/tour/hello重定向到http://localhost/tour.php?name=hello

我在我的 .htaccess 中尝试了以下内容

RewriteEngine on

RewriteCond %{REQUEST_URI} ^/tour/(.+)/?$
RewriteRule ^tour/(.+)/?$ tour.php?name=$1 [L,QSA, NC]

它重定向到http://localhost/tour.php但 $_GET["name"] 始终为 NULL

如果我将目标页面更改为 tour.php 以外的任何内容,它就可以工作

RewriteRule ^tour/(.+)/?$    handler.php?name=$1 
RewriteRule ^tour/(.+)/?$    blah.php?name=$1

所有工作

标签: apache.htaccessmod-rewrite

解决方案


您需要通过以下方式禁用内容协商

Options -MultiViews

在您的 .htaccess 中。

AcceptPathInfo off

一些解释:

第一个问题是内容协商。假设您有两个名为 tour.txt 和 tour.php 的文件,如果启用了内容协商,如果您的 URL 只是http://localhost/tour,没有扩展名,Apache 正在寻找具有任何扩展名并基于客户端的名为 tour 的文件首选项(特别是本示例的 Accept HTTP 标头),将尝试在 tour.txt 和 tour.php 之间找到最佳匹配以提供给客户端。

您的问题的第二个元素是 AcceptPathInfo:启用后,Apache 接受 URL 路径末尾的多余部分以将其填充为内部变量(例如 PHP 中众所周知$_SERVER['PATH_INFO']的)。为了说明,假设您有一个文件 tour.php。因此,使用AcceptPathInfo on, http://localhost/tour.php/extra/path调用 tour.php 并使用$_SERVER['PATH_INFO']= /extra/path 而不是像通常预期的那样被视为不存在 (404) /with AcceptPathInfo off

现在,将 AcceptPathInfo 和内容协商结合起来:问题是您的规则尝试“拦截”以 tour/ 开头的内容,但 tour.php 存在 [是一个文件],因此它与您自己的规则冲突,因为路径 tour/foo [对于 URL http://localhost/tour/foo]首先由内容协商解析为 tour.php/foo 和“接受”为 tour.php 和$_SERVER['PATH_INFO']= /foo 感谢 AcceptPathInfo。结论:在这种非常具体的情况下,没有发生重写,但看起来如此,因为内容协商 + AcceptPathinfo 通过登陆 tour.php 具有相同的效果,这就是为什么您没有得到您期望的查询字符串(名称=富)。

另请注意,规则中的 NC 标志之前不应有任何空格。


推荐阅读