为啥我的 301 重定向需要这么长时间?

Posted

技术标签:

【中文标题】为啥我的 301 重定向需要这么长时间?【英文标题】:Why is my 301 Redirect taking so long?为什么我的 301 重定向需要这么长时间? 【发布时间】:2011-05-27 17:29:40 【问题描述】:

在为加快我的网站速度而进行的长期疲惫的追求中,我发现重定向有问题:目前我的 index.php 通过 PHP 标头位置 301 永久重定向处理所有主页重定向: website.com >> website.com/en/homewebsite.de >> website.de/de/home etcettera etcettera(这个多语言网站大约需要 20 个)从 200 毫秒到 6000 毫秒的任何时间来进行重定向。看看瀑布!

之后,页面在闪电般的眨眼间加载! 你不会说什么浪费时间?服务器一直在做什么? 仔细检查后,我的最佳猜测是:它正在洗衣

我几乎为此放弃了 PHP! 非常欢迎任何和所有关于我令人费解的问题的线索 +1

A.给定事实:Apache/2.0.54 Fedora,PHP 5.2.9。没有数据库:只有包含大约 15 个 php 的扁平 php 文件,这些文件用页眉和页脚完成了我的页面)。 Y慢等级:92/100!好页面速度:93/100! javascript和css尽可能结合。缓存控件似乎也设置得很好(由成绩证明)。 100 分中有 7 分缺少什么:不使用 Keep-Alive(超出我对共享主机的控制范围,不使用 Content Delivery Network。我可以忍受缺少的 7 分,但这是对速度的重大打击!

B.此外:我最近在这里得到了很好的见解,我应该通过 htacces 使用 url 重写。点了,但是,也许这里有一些 else 错误,我应该在继续对我来说更困难的 apache 正则表达式语法之前进行更正。

C.更快的方式:当我 php include 预期的主页时,而不是重定向,然后所有加载都很快,但 url 没有被重写:它位于浏览器栏上的 website.com,而我希望在包含它之后成为网站。 com/en/home。这可以用PHP吗?要包含+更改url的当前地址吗?

结论:您可以使用 index.php 或使用 .htaccess 进行重定向。从我的测试来看(来自下面的天才答案!谢谢大家!)后者的速度似乎无与伦比:重定向比 php 重定向快得多!将重定向减少到比第一次 dns 查找更短。

see here how to do this correclty for multilingual site

【问题讨论】:

发送标头不会停止脚本运行。 支持这张瀑布图片。 没有一个,也没有两个,而是三个徒手画圈。我的天啊。瞬间喜欢。 @Bolt 真的吗?如果图像太小而无法阅读,我通常会尝试单击它。这真的需要指出吗? :) 这一定是我在 SO 上见过的最好的图片之一。 【参考方案1】:

该死,我讨厌被这种问题困住。您需要消除一些变量。

首先我应该指出,PHP 不会刷新所有自己的标头,直到您开始输出内容(或者,如果 output_buffering(?) ini 指令设置为 x 字节,直到您输出 x 字节)。所以下面的脚本直到最后才会完成“发送标头”:

<?php
header('Content-Type: text/pants');
sleep(6);
header('Ding-Ding: time to put the socks in the dryer');
echo "z"; // headers are sent here

如果您将exit;echo "wheeeee"; exit; 放在PHP 脚本的最顶部,调用en/home 会发生什么情况?那么当你用一个普通的空文件替换它时会发生什么?如果带有exit 的php 脚本速度很慢,但纯文本文件速度很快,那么PHP 解释器可能正在玩有趣的虫子。如果您仍然得到两者的延迟,那么您已经消除了实际响应生成的原因(但如果是这种情况,我仍在尝试提出一些想法)。

另外,你可以 ssh 到服务器吗?如果是这样,您可以尝试从服务器内部获取相同的页面吗?如果你可以没有速度问题,我会看客户端。如果你不能 SSH,你可以尝试从 PHP 发出请求,虽然我真的不确定这是否可行:

<?php
$context = stream_context_create(array(
    'http'=>array(
        // send request headers if you need to
        'header'=>array(
            'Foo: Bar',
            'Bar: Baz',
        ),
    ),
));
$start = microtime(true);
$response = file_get_contents('http://yourserver.com/', null, context);
$end = microtime(true) - $start;
var_dump($end);

// for some bizarre reason, PHP emits this variable into the local scope.
var_dump($http_response_header);

您是否尝试过从其他机器或世界其他地方发出相同的请求?这可以确认或否认它只是你的机器。

如果是响应生成,您可以尝试的另一件事是在生产服务器上进行一些 hack-profiling。我讨厌不得不做这些事情,但有时您的代码只是拒绝在生产服务器上运行,就像它在您的开发环境或暂存中运行一样。对生成/en/home的脚本执行此操作:

<?php
// put this at the very top
$rqid = uniqid('', true);
$h = fopen(__DIR__.'/crap.log', 'a');
fwrite($h, $rqid.' [START] '.microtime(true).PHP_EOL);
fclose($h);

// do all that other wonderful stuff, like laundry or making a cup of tea

// put this at the very end
$h = fopen(__DIR__.'/crap.log', 'a');
fwrite($h, $rqid.' [END]   '.microtime(true).PHP_EOL.PHP_EOL);
fclose($h);

针对它运行一些请求,检查以确保“crap.log”正在向其写入内容(检查权限!!),然后您将获得一些数据来显示您的脚本中是否存在某些内容这需要进一步调查作为缓慢的原因。

哦,我有提到 mysql 索引吗?您在请求期间是否有任何查询?您是否为表添加了所有正确的索引?

Steven Xu 在 cmets 中为您的问题提出了一个很好的观点 - 您确定您用来生成瀑布的程序为您提供了很好的信息吗?如果你还没有安装Firebug,请尝试安装,点击firefox右下角的小萤火虫图标,确保“网络”面板是打开的,然后重新运行你的请求,看看瀑布是否与结果一致您在使用的程序中看到的。

另外,我知道这是一个愚蠢的建议,我很抱歉,但我认为需要说明的是:您的主机不允许 ssh 并且只使用 PHP 4?我会认真考虑另一位主机。它甚至可以解决这个特定的问题。

我会根据我的想法添加更多内容。

【讨论】:

只需阅读您更新后的答案 Shabbyrobe。非常有见地。输出缓冲的特殊事物。只是为了确定:目前在我的 htacces 中有这个:&lt;ifmodule mod_php4.c&gt; php_value zlib.output_compression 16386 &lt;/ifmodule&gt; 我猜那是别的,对吧?我的 php.ini 说:output_buffering = Off 我想我可以把它关掉,对吧?我无法访问命令行,那是 ssh 吗?另一个问题:如果我在找到与标头位置匹配后立即执行exit();,那么所有关于缓冲区的担忧都过去了? 您只使用 PHP 4?如果客户端可以处理,该配置指令使 PHP gzip 成为输出。这是一件好事,将大大有助于您获得高 YSlow 分数。 zlib.output_compression (php.net/manual/en/zlib.configuration.php) 的手册建议如果使用整数值,则输出会缓冲那么多字节。因此,您的设置意味着 PHP 将在刷新标头之前将您回显的前 16kb 数据排队。我不认为这是你的问题的原因。当脚本执行结束时缓冲区也会刷新。【参考方案2】:

如果确实是标题需要很长时间,那么你的 JS/CSS/html 就无关紧要了。

您可以在.htaccess进行转发。

RewriteEngine On
RewriteRule ^$ en/home [R=301]

这将本质上发送相同的标头,但它不会首先调用 PHP 引擎来执行它:)

更新

在我看来,您的en/home 页面需要更长的时间来下载。

【讨论】:

en/home 页面需要较长时间才能开始下载。延迟可能是由于 PHP 或数据库服务器首次启动。 谢谢,我更新了我的问题。您会看到它是一个多语言网站,要求每个域去自己的家,所以恐怕一个家庭的所有域是不够的。 @Sam 啊,是的,我没想到。 :)【参考方案3】:

我认为 Ignacio Vazquez-Abrams 可能有答案:在调用 header() 进行重定向后,您需要调用 exit() 以停止 PHP 脚本执行。否则,脚本将继续执行,将输出发送到浏览器,直到结束。由于浏览器必须等待服务器端脚本结束才能执行可能导致问题的重定向。

更新

刚刚阅读亚历克斯的更新,他似乎是正确的。 /en/home 页面是时间所在。

【讨论】:

现在我明白 Ignacio 的意思了,Jeremy:我必须手动停止脚本,因为它不需要继续运行 index.php 文件的所有其余部分。这是正确的实现吗?:case "website.bla": header('HTTP/1.1 301 Moved Permanently'); header('Location: /bla/home'); exit(); 然后是下一个案例等。 山姆,看起来不错。试一试,看看它是否适合你。顺便说一句,您不需要两次调用标头。您可以使用函数的第二个和第三个参数来设置一次命中的重定向类型,有点像这样: header('Location: /bla/home', true, 301); @Sam:有什么理由你在这里使用 301 而不是 302 (另外,如果你指定一个位置标头,nb PHP 会自动默认为 302,除非你明确声明你的 HTTP 状态就像你在这里做的那样)?似乎您的情况在语义上是 302,而不是 301。您有一个规范的基本 URL(根),3xx 重定向到您当前的处理程序。今天,您的 CMS 使得 /en/home 变得有意义。明天,可能不会。但是 / 将永远是你的入口路径。对我来说似乎是302。但这完全是另一个问题。 其实搜索引擎不喜欢的是302重定向。一段时间后,他们开始认为发生了一些可疑的事情(302 应该只是一个临时重定向),这可能会影响页面排名。通过 301 重定向,搜索引擎将从索引中删除旧页面并将其替换为被重定向的页面。您需要考虑这些选项中的任何一个将如何影响您的排名。搜索引擎蜘蛛遇到 301 重定向的可能性有多大? Jeremy,请查看有问题的更新:我将exit(); 放在break; 之前还是之后?

以上是关于为啥我的 301 重定向需要这么长时间?的主要内容,如果未能解决你的问题,请参考以下文章

nginx为啥返回301重定向

为啥我的 .htaccess 文件的 301 重定向不能正常工作?

为啥我的 .htaccess 301 重定向规则不起作用?

为啥这个 301 重定向会导致重定向到错误的 url?

301重定向:为啥连接关闭?

为啥jQuery会阻止propper 301重定向?