跳到主要内容

如何修复 PHP 重定向问题

问题

www.yourdomain.ext/default.htmlindex.html 重定向到 http://www.yourdomain.ext/index.php?act=whatever 时,结果却重定向到 **http://www.yourdomain.ext/index.php%3fact=whatever**。换句话说,为什么地址栏中的 "?" 被替换为 "%3f"(或其他任何东西)?为什么你的 PHP 重定向不工作? 下面的解决方案将展示如何使用 Apache 的 mod_rewrite 模块修复 PHP 重定向问题。mod_rewrite 模块是一个允许你使用无限数量的规则来操作 URL 的工具。你可以为每个规则附加无限数量的规则条件。这使你能够通过考虑服务器变量、HTTP 头、环境变量或时间戳来重写 URL。

解决方案

要修复 PHP 重定向问题,你可以使用 mod_rewrite 模块将一个 URL 重定向到另一个 URL,将长 URL 转换为短 URL,重定向缺失页面等。 开始重写:

# 现在开始进行动态处理,见..
RewriteRule ^(.*).html $1.php

每当你使用 mod_rewrite 时,你只需在每个 .htaccess 文件中执行一次:

Options +SymlinksIfOwnerMatches
RewriteEngine on

在任何 Rewrite 规则之前,+FollowSymLinks 必须启用,才能使任何规则生效。这是重写引擎的安全要求。通常,它在根目录中启用,你不需要添加它。 以下行为该文件夹启用重写引擎。如果此指令在你的主 .htaccess 文件中,则理论上重写引擎对你整个网站启用。始终在编写任何重定向之前添加该行是明智的。虽然本页上的一些指令可能看起来分成两行,但在你的 .htaccess 文件中,它们必须完全在一行上。 注意: 有关如何进行 PHP 头重定向和使用 PHP 头重定向的最佳实践的说明,请查看文章:如何编写 PHP 重定向 - PHP 头重定向。


  • 简单重写
  • 复杂重写
  • 缩短 URL
  • httpd.conf
  • 继承
  • 总结

简单重写

Apache 扫描所有传入的 URL 请求,检查你的 .htaccess 文件中的匹配项,并将这些匹配的 URL 重写为你指定的内容。例如:

# 所有请求到 whatever.html 将被发送到 whatever.php:
Options +SymlinksIfOwnerMatches
RewriteEngine on
RewriteRule ^(.*).html $1.php [nc]

这对于任何将网站从静态 HTML(你可以使用 .html 或 .htm(.*))更新为动态 PHP 页面的人来说是有帮助的,旧页面的请求会自动重写为你的新 URL。访客和搜索引擎都可以以任何方式访问你的内容。此外,这使你能够将 PHP 代码及其包含的 HTML 结构分成两个单独的文件,从而使编辑和更新变得容易。结尾的 [nc] 部分表示 "不区分大小写" 或 "大小写不敏感"。 用户可以链接到 whatever.htmlwhatever.php,但他们总是会得到 whatever.php,而 whatever.html 不存在。 这可能会很棘手。访客在浏览器地址栏中仍然会看到 whatever.html,并且仍然会继续收藏你的旧 .html URL。这是一个 PHP 重定向循环。搜索引擎也会继续将你的链接索引为 .htm。有人甚至认为,从两个不同地方提供相同内容可能会导致搜索引擎对你进行惩罚。这可能会让你感到困扰,也可能不会,但如果你在意,mod_rewrite 可以帮助:

# 这将进行一个 "真实" 的 HTTP 重定向:
Options +SymlinksIfOwnerMatches
RewriteEngine on
RewriteRule ^(.+).htm$ http://yourdomain.ext/$1.php [r=301,nc]

这次,我们指示 mod_rewrite 发送一个适当的 HTTP(永久移动)重定向,也称为 301 重定向。用户的浏览器会被物理重定向到一个新 URL,并且 whatever.php 会出现在他们的浏览器地址栏中,搜索引擎和其他实体会自动将它们的链接更新为 .php 版本。

复杂重写

你可能已经注意到,上面的示例使用正则表达式来匹配变量。这意味着它匹配 (.+) 内部的部分,并使用它来构造新 URL 中的 "$1"。换句话说,(.+) = $1,你可以有多个 (.+) 部分,对于每个部分,mod_rewrite 会自动在目标 URL 中创建匹配的 $1、$2、$3 等。例如:

# 一个更复杂的重写规则:
Options +SymlinksIfOwnerMatches
RewriteEngine on
RewriteRule ^files/(.+)/(.+).zip download.php?section=$1&file=$2 [nc]

这将允许你将链接呈现为:

http://mysite/files/games/hoopy.zip

并在后台将其转换为:

http://mysite/download.php?section=games&file=hoopy

某些脚本可以处理这个。许多搜索引擎根本不跟踪我们的 ?generated=links。因此,如果你创建生成页面,这将很有用。谷歌可以处理 URL 中的许多参数而不会出现任何问题。然而,其他搜索引擎则不会。 将链接呈现为/standard/paths 意味着用户在输入 URL 时更不容易出现拼写错误。

# 一个更复杂的重写规则:
Options +SymlinksIfOwnerMatches
RewriteEngine on
RewriteRule ^blog/([0-9]+)-([a-z]+) http://yourdomain.ext/blog/index.php?archive=$1-$2 [nc]

使用上述代码,任何人都可以通过在浏览器中输入:

http://yourdomain.ext/blog/2003-nov

自动将其服务器端转换为:

http://yourdomain.ext/blog/index.php?archive=2003-nov

这是 yourdomainblog 可以理解的。很容易看出,你可以通过对 POSIX 正则表达式的基本理解来执行一些 URL 操作。

缩短 URL

mod_rewrite 的一个常见用途是缩短 URL。较短的 URL 更容易记住,也更容易输入。例如:

# 请注意正则表达式的使用:
Options +SymlinksIfOwnerMatches
RewriteEngine On
RewriteRule ^grab(.*) /public/files/download/download.php$1

此规则将用户的 URL 服务器端转换为:

http://mysite/grab?file=my.zip

变为:

http://mysite/public/files/download/download.php?file=my.zip

通过这种技术,你可以将 /public/files/download/ 移动到你网站上的任何其他位置,所有旧链接仍然可以正常工作。只需更改你的 .htaccess 文件以反映新位置。编辑一行,你就完成了。

http://yourdomain.ext/img/hotlink.png [nc]

你可能会看到最后一行被分成两行,但它实际上是一行(本页上的所有指令都是)。所以,看看它的作用。 我们首先像往常一样启用重写引擎。 第一条 RewriteCond 行允许直接请求(不是来自其他页面 - "空引用")不受影响地通过。下一行表示如果浏览器确实发送了引用头,并且其中的域名部分不包含 "yourdomain",那么 DO 重写此请求。 最后一条 RewriteRule 行指示 mod_rewrite 重写所有匹配的请求(任何在其引用中没有 "yourdomain" 的请求),请求 gifs、jpegs 或 pngs 到一个替代图像。

httpd.conf

请记住,如果你将这些规则放在主服务器配置文件(通常是 httpd.conf)中,而不是 .htaccess 文件中,你需要在 RewriteRule 行的开头使用 ^/... .****.. 而不是 ^... ...,换句话说,添加一个斜杠。但由于 httpd.conf 受到访问限制,这在 Bluehost 上不适用。

继承

如果你在网站的子文件夹中创建规则,请阅读以下内容。 请注意,顶级文件夹中的规则适用于这些文件夹内的所有文件夹。我们称之为 "继承"。通常,这样工作。然而,如果你开始在子文件夹中创建其他规则,你将由于继承或你更喜欢的后代而覆盖已经适用于该文件夹的规则,仅适用于该子文件夹的规则。例如: 假设你在主 /.htaccess 中有一个规则,将以 .html 结尾的文件请求重定向到其 .php 等效项,就像之前的示例一样。现在,如果你需要向 /osx/.htaccess 文件添加一些重写规则,.html >> .php 的重定向将不再适用于 /osx/ 子文件夹。你必须重新插入它,但有一个关键的区别。

# 这在我的主 .htaccess 文件中正常工作
# 主(顶级) .htaccess 文件..
# 请求 file.html 转到 file.php
Options +SymlinksIfOwnerMatches
RewriteEngine on
RewriteRule ^(.*).htm$ http://yourdomain.ext/$1.php [r=301,nc]

以下是更新的 /osx/.htaccess 文件,重新插入了 .html >> .php 重定向规则,但你需要重新插入规则以使其在此子文件夹中工作:

# /osx/.htaccess 文件..
Options +SymlinksIfOwnerMatches
RewriteEngine on
RewriteRule 我需要在这里的一些规则
RewriteRule 我需要的其他规则
RewriteRule ^(.*).htm$ http://yourdomain.ext/osx/$1.php [r=301,nc]

找出子文件夹规则中的差异。你必须将当前路径添加到新规则中。现在它又可以工作了。如果你记住这一点,你可以在任何地方复制重写规则。

总结

mod_rewrite 允许你将浏览器从任何地方发送到任何地方。此外,你可以根据请求的 URL 和 IP 地址、浏览器代理(例如,将旧浏览器发送到不同页面)甚至时间段创建规则。 mod_rewrite 语法的细节是比这更长的文档的主题。你可以尝试更高级的重写规则。请查看 Apache 文档以获取更多信息。如果你在某个 *nix 操作系统上运行(如果你在任何操作系统上安装了 Apache),你的机器上可能会有一份 Apache 手册。查看这些优秀的 mod_rewrite 语法指南。

http://www.ilovejackdaniels.com/apache/mod_rewrite-cheat-sheet/
http://httpd.apache.org/docs/1.3/mod/mod_rewrite.html
http://httpd.apache.org/docs/1.3/misc/rewriteguide.html
http://forum.modrewrite.com/