如何修复PHP中的“标头已发送”错误

How to fix "Headers already sent" error in PHP

提问人: 提问时间:11/7/2011 最后编辑:17 revs, 9 users 59%Moses89 更新时间:10/8/2023 访问量:1951899

问:

这个问题的答案是社区的努力。编辑现有答案以改进此帖子。它目前不接受新的答案或交互。

运行脚本时,我收到几个错误,如下所示:

警告:无法修改标头信息 - 第 23 行 /some/file.php 中的标头已由(输出从 /some/file.php:12 开始)发送

错误消息中提到的行包含 header() 和 setcookie() 调用。

这可能是什么原因?以及如何解决它?

php http 标头

评论

0赞 Jack 4/4/2014
确保没有输出任何文本(并且可能在这里被证明是有用的)。然后,您可以将 cookie 或会话设置为等于,然后用于清除缓冲区。ob_startob_end_clean() ob_get_contents()ob_end_clean()
5赞 T.Todua 9/19/2014
~~~~~~~~~~ 您的文件编码不应是 ,而是UTF-8UTF-8 (Without BOM)~~~~~~~~~~~

答:

10赞 AJ. 12/16/2009 #1

php 标签之外可能存在空格。

9赞 Anon. 12/16/2009 #2

your 和 tags 之间的空行将发送到客户端。?><?php

当发送第一个标头时,它会导致首先发送标头。

一旦发生这种情况,您将无法再修改标头。

删除那些不必要的标签,将它们全部放在一个大块中。<?php

42赞 Amber 12/16/2009 #3

第 45-47 行:

?>

<?php

这是发送几个换行符作为输出,因此标头已经调度。只需删除这 3 行(毕竟它都是一个大的 PHP 块,无需结束 PHP 解析然后重新开始),以及第 60-62 行的类似块,它就会起作用。

请注意,您收到的错误消息实际上为您提供了很多信息,以帮助您自己找到它:

警告:无法修改标头 information - 已发送的标头 (输出开始于 C:\xampp\htdocs\speedycms\deleteclient.php:47) 在 C:\xampp\htdocs\speedycms\deleteclient.php 中 上线 106

两个粗体部分告诉您在标头之前发送输出的项目在哪里(第 47 行),以及在输出之后尝试发送标头的项目在哪里(第 106 行)。

56赞 Chris Gutierrez 12/16/2009 #4

在启动会话之前,脚本有意外输出时,通常会发生这种情况。使用您当前的代码,您可以尝试使用输出缓冲来解决它。

尝试在脚本的最顶部和文档的最末尾添加对函数的调用。ob_start();ob_end_flush();

26赞 staticboy 9/30/2011 #5

检查文档编码

我也有同样的问题。我在 Windows XP 上使用 Notepad++ 和 WampServer 在本地运行 Apache 进行开发,一切都很好。上传到在Unix上使用Apache的托管服务提供商后,我收到了此错误。我没有额外的PHP标签,也没有结束标签后额外行的空格。

对我来说,这是由文本文档的编码引起的。我在Notepad++中使用了“在没有BOM的情况下转换为UTF-8”选项(在编码选项卡下)并重新加载到Web服务器。问题已修复,无需代码/编辑更改。

215赞 phihag 11/7/2011 #6

在发送 HTTP 标头(使用 setcookieheader)之前发送任何内容时,会触发此错误消息。在 HTTP 标头之前输出内容的常见原因是:

  • 意外空格,通常位于文件的开头或结尾,如下所示:

       <?php
      // Note the space before "<?php"
      ?>
    

为了避免这种情况,只需省略关闭 - 无论如何都不需要。?>

  • php 文件开头的字节顺序标记。使用十六进制编辑器检查您的 php 文件,看看是否是这种情况。它们应该以字节 开头。您可以安全地从文件开头删除 BOM。3F 3CEF BB BF
  • 显式输出,例如调用 、 、 、 、 代码 before 等。echoprintfreadfilepassthru<?
  • 如果设置了 display_errors php.ini 属性,则由 php 输出的警告。php 不会因程序员错误而崩溃,而是静默修复错误并发出警告。虽然您可以修改display_errorserror_reporting配置,但您应该解决问题。
    常见原因是访问数组的未定义元素(例如不使用isset 来测试是否设置了输入),或者使用未定义的常量而不是字符串文字(如 中,请注意缺少的引号)。
    $_POST['input']$_POST[input]

打开输出缓冲应该可以使问题消失;调用 ob_start 后的所有输出都缓冲在内存中,直到您释放缓冲区,例如使用 ob_end_flush

但是,虽然输出缓冲可以避免这些问题,但您确实应该确定为什么应用程序在 HTTP 标头之前输出 HTTP 正文。这就像接一个电话,讨论你的一天和天气,然后告诉来电者他拿错了号码。

43赞 Seth Carnegie 11/7/2011 #7

是吗

printf ("Hi %s,</br />", $name);

在设置 cookie 之前,这是不允许的。您不能在标头之前发送任何输出,甚至不能发送空行。

34赞 Sarfraz 11/7/2011 #8

正是因为这样一句话:

printf ("Hi %s,</br />", $name);

在发送标题之前,不应打印/回显任何内容。

3312赞 mario 11/7/2011 #9

发送标头之前没有输出!

在进行任何输出之前,必须调用发送/修改 HTTP 标头的函数。 否则,调用将失败:summary ⇊

警告:无法修改标头信息 - 标头已发送(输出从 script:line 开始)

修改 HTTP 标头的一些函数包括:

输出可以是:

  • 故意:

    • print和其他产生输出的函数echo
    • 原始部分先前的代码。<html><?php

为什么会这样?

要了解为什么必须在输出之前发送标头,有必要 以查看典型的 HTTP 响应。PHP 脚本主要生成 HTML 内容,但也传递一个 Web 服务器的 HTTP/CGI 标头集:

HTTP/1.1 200 OK
Powered-By: PHP/5.3.7
Vary: Accept-Encoding
Content-Type: text/html; charset=utf-8

<html><head><title>PHP page output page</title></head>
<body><h1>Content</h1> <p>Some more output follows...</p>
and <a href="/"> <img src=internal-icon-delayed> </a>

页面/输出始终跟在标题后面。PHP 必须通过 标头首先发送到 Web 服务器。它只能这样做一次。 在双换行符之后,它再也无法修改它们了。

当 PHP 收到第一个输出 (, , ) 时,它将刷新所有收集的标头。之后,它可以发送所有输出 它想要。但是,发送更多的 HTTP 标头是不可能的。printecho<html>

你怎么能找出过早输出发生的位置?

该警告包含所有相关信息 找到问题原因:header()

警告:无法修改标头信息 - 标头已由 (输出从 /www/usr2345/htdocs/auth.php:52) 发送 /www/usr2345/htdocs/index.php 上线 100

这里的“第 100 行”是指调用失败的脚本。header()

括号内的“输出开始于”注释更重要。 它以先前输出的来源为单位。在此示例中,它是第 52。这就是您必须寻找过早输出的地方。auth.php

典型原因:

  1. 打印、回声

    有意输出的 和 语句将终止发送 HTTP 标头的机会。为了避免这种情况,必须对应用程序流进行重组。使用函数和模板方案。确保呼叫先于消息发生 被写出来了。printechoheader()

    产生输出的函数包括

    • print, , ,echoprintfvprintf
    • trigger_error, , , ,ob_flushob_end_flushvar_dumpprint_r
    • readfile, , , ,passthruflushimagepngimagejpeg


    除其他外,还有用户定义的函数。

  2. 原始 HTML 区域

    文件中未解析的 HTML 部分也是直接输出。 必须注意将触发调用的脚本条件 在任何原始块之前。.phpheader()<html>

    <!DOCTYPE html>
    <?php
        // Too late for headers already.
    

    使用模板方案将处理与输出逻辑分开。

    • 将表单处理代码放在脚本之上。
    • 使用临时字符串变量延迟消息。
    • 实际输出逻辑和混合 HTML 输出应排在最后。

  3. “script.php 第 1 行”警告前的空格<?php

    如果警告指的是内联输出 1,则主要是 在开始标记之前引导空格、文本或 HTML。<?php

     <?php
    # There's a SINGLE space/newline before <? - Which already seals it.
    

    同样,对于附加的脚本或脚本部分,也可能发生这种情况:

    ?>
    
    <?php
    

    PHP 实际上在关闭标记后占用了一个换行符。但事实并非如此 补偿移动到此类间隙中的多个换行符或制表符或空格。

  4. UTF-8 BOM

    换行符和空格本身就是一个问题。但也有“看不见” 可能导致此问题的字符序列。最著名的是 UTF-8 BOM(字节顺序标记),大多数文本编辑器都不显示它。它是字节序列,对于UTF-8编码的文档来说,它是可选的和冗余的。但是,PHP 必须将其视为原始输出。它可能显示为输出中的字符(如果客户端将文档解释为 Latin-1)或类似的“垃圾”。EF BB BF

    特别是图形编辑器和基于 Java 的 IDE 忽略了它 存在。他们不会将其可视化(Unicode 标准强制要求)。 然而,大多数程序员和控制台编辑器会这样做:

    joes 编辑器显示 UTF-8 BOM 占位符,MC 编辑器显示一个点

    在那里,很容易及早发现问题。其他编辑可能会识别 它存在于文件/设置菜单中(Windows 上的 Notepad++ 可以识别和解决问题), 检查 BOM 是否存在的另一种选择是求助于十六进制编辑器。 在 *nix 系统上,hexdump 通常可用, 如果不是简化审计这些问题和其他问题的图形变体:

    Beav HexEditor 显示 UTF-8 BOM

    一个简单的解决方法是将文本编辑器设置为将文件另存为“UTF-8(无 BOM)” 或与此类命名法相似。通常,新手会求助于创建新文件,然后将以前的代码复制并粘贴回去。

    更正实用程序

    还有用于检查和重写文本文件的自动化工具 (sed/awk 或 )。 特别是对于 PHP,有 phptags 标签 tidier。 它将关闭和打开的标签重写为长格式和短格式,但也很容易 修复了前导和尾随空格、Unicode 和 UTF-x BOM 问题:recode

    phptags  --whitespace  *.php
    

    在整个包含目录或项目目录上使用是安全的。

  5. 后面的空格?>

    如果错误源被提及为结束 ?> 后面,那么这就是写出一些空格或原始文本的地方。 此时,PHP 结束标记不会终止脚本执行。之后的任何文本/空格字符都将作为页面内容写出 还。

    通常建议,特别是对于新手,尾随 PHP 应省略关闭标记。这避免了这些案例的一小部分。 (通常脚本是罪魁祸首。?>include()d

  6. 错误源显示为“第 0 行未知”

    如果没有错误源,它通常是 PHP 扩展或 php.ini 设置 是具体化的。

    • 有时是流编码设置ob_gzhandlergzip
    • 但它也可以是任何双负载模块 生成隐式 PHP 启动/警告消息。extension=

  7. 前面的错误消息

    如果另一个 PHP 语句或表达式导致警告消息或 注意被打印出来,这也算作过早输出。

    在这种情况下,您需要避免错误, 延迟语句执行,或用例如 isset() 或 @() 来抑制消息 - 当其中任何一个不妨碍以后的调试时。

无错误消息

如果您拥有或禁用了每 , 则不会显示任何警告。但是忽略错误不会使问题消失 離開。过早输出后仍无法发送标头。error_reportingdisplay_errorsphp.ini

因此,当重定向静默失败时,它非常 建议探查警告。使用两个简单的命令重新启用它们 在调用脚本之上:header("Location: ...")

error_reporting(E_ALL);
ini_set("display_errors", 1);

或者,如果所有其他方法都失败了。set_error_handler("var_dump");

说到重定向标头,您应该经常使用类似 这对于最终代码路径:

exit(header("Location: /finished.html"));

最好是实用程序函数,它打印用户消息 以防万一。header()

输出缓冲作为解决方法

PHP 输出缓冲是缓解此问题的解决方法。它通常可靠地工作,但不应该 替代适当的应用程序结构并将输出与控制分离 逻辑。它的实际目的是最大限度地减少到 Web 服务器的分块传输。

  1. 然而,output_buffering= 设置可能会有所帮助。 在php.ini中或通过.htaccess甚至.user.ini配置它 现代 FPM/FastCGI 设置。
    启用它将允许 PHP 缓冲输出,而不是立即将其传递给 Web 服务器。因此,PHP 可以聚合 HTTP 标头。

  2. 同样,它也可以与调用脚本顶部的 ob_start(); 调用。然而,由于多种原因,这不太可靠:

    • 即使启动第一个脚本,空格或 BOM 之前可能会被打乱,使其无效<?php ob_start(); ?>

    • 它可以隐藏 HTML 输出的空格。但是,一旦应用程序逻辑尝试发送二进制内容(例如生成的图像), 缓冲的无关输出成为一个问题。(需要作为进一步的解决方法。ob_clean()

    • 缓冲区的大小有限,当保留默认值时,很容易溢出。 这种情况也不少见,很难追踪到。

因此,这两种方法都可能变得不可靠 - 尤其是在 开发设置和/或生产服务器。这就是输出缓冲 被广泛认为只是一根拐杖/严格来说是一种解决方法。

另请参阅手册中的基本使用示例,以及更多优缺点:

但它在另一台服务器上工作!?

如果您之前没有收到标头警告,则输出缓冲 php.ini 设置已更改。它可能在当前/新服务器上未配置。

检查headers_sent()

如果出现以下情况,您始终可以使用 headers_sent() 来探测 还是有可能...发送标头。这对于有条件地打印很有用 info 或应用其他回退逻辑。

if (headers_sent()) {
    die("Redirect failed. Please click on this link: <a href=...>");
}
else{
    exit(header("Location: /user.php"));
}

有用的回退解决方法包括:

  • HTML 标签<meta>

    如果您的应用程序在结构上难以修复,那么一个简单的(但 有点不专业)允许重定向的方法是注入 HTML 标签。可以通过以下方式实现重定向:<meta>

     <meta http-equiv="Location" content="http://example.com/">
    

    或者有短暂的延迟:

     <meta http-equiv="Refresh" content="2; url=../target.html">
    

    这会导致在超过该部分使用时出现无效的 HTML。 大多数浏览器仍然接受它。<head>

  • JavaScript 重定向

    作为替代方案,JavaScript 重定向可用于页面重定向:

     <script> location.replace("target.html"); </script>
    

    虽然这通常比解决方法更符合 HTML, 它会导致对支持 JavaScript 的客户端的依赖。<meta>

然而,当真正的 HTTP header() 调用失败。理想情况下,您始终将其与用户友好的消息和 可点击的链接是最后的手段。(例如,这就是 http_redirect() PECL 扩展的作用。

为什么和也受到影响setcookie()session_start()

两者都需要发送 HTTP 标头。 因此,相同的条件适用,并且将生成类似的错误消息 用于过早输出的情况。setcookie()session_start()Set-Cookie:

(当然,它们还会受到浏览器中禁用的 cookie 的影响 甚至是代理问题。会话功能显然也依赖于免费 磁盘空间和其他 php.ini 设置等)

更多链接

评论

0赞 Teson 12/3/2015
此外,常规记事本.exe也很棘手。我通常使用不添加 BOM 的 NetBeans,即使文件是这样编码的。稍后在记事本中编辑文件会把事情搞砸,尤其是对于作为 Web 服务器的 IIS。似乎 apache 丢弃了(统一添加的)BOM。
4赞 Nikita 웃 2/9/2016
从 php 文件末尾删除关闭通常是一种很好的做法,这也有助于最大限度地减少这些错误。文件末尾不会出现不需要的空格,您以后仍然可以将标头添加到响应中。如果您使用输出缓冲,并且不希望在包含的文件生成的部分末尾添加不需要的空格,这也非常方便。?>
1赞 mario 8/2/2017
@PeterSMcIntyre UTF8 BOM 大概(修复)/未启用输出缓冲(不要依赖它)。
2赞 kmuenkel 6/17/2021
您可能只想添加一个细节。 如果错误消息省略了该信息,将告诉您发送标头的确切内容。不知道为什么,但有时似乎是这样。$file = $line = null; headers_sent($file, $line); die("$file:$line");
1赞 Jānis Elmeris 11/8/2022
关于情况 6,错误的未知来源可能与浏览器中止的请求有关(在访问日志中查找 HTTP 状态 499,尽管它可能并非在所有情况下都出现)。 在这种情况下,可以修复/解决“标头已发送”通知,它至少对我有用。ignore_user_abort=On
27赞 Sliq 5/17/2012 #10

一个简单的提示:脚本中一个简单的空格(或不可见的特殊字符),就在第一个标签之前,可能会导致这种情况! 特别是当你在一个团队中工作时,有人正在使用一个“弱”的IDE,或者用奇怪的文本编辑器弄乱了文件。<?php

我;)看到了这些事情

137赞 Manish Shrivastava 8/1/2012 #11

我以前遇到过很多次这个错误,我敢肯定所有PHP程序员以前都至少遇到过一次这个错误。

可能的解决方案 1

此错误可能是由文件开始之前或文件结束之后的空格引起的。这些空白区域不应该在这里。

例如)

<!-- THERE SHOULD BE NO BLANK SPACES HERE -->
<?php  

   echo "your code here";

?>
<!-- THERE SHOULD BE NO BLANK SPACES HERE -->

检查与导致此错误的文件关联的所有文件。

注意:有时 EDITOR(IDE) 如 gedit(默认的 Linux 编辑器)会在保存文件上添加一个空行。这不应该发生。如果您使用的是 Linux。您可以使用VI编辑器删除页面末尾?>后面的空格/行。

可能的解决方案 2:如果不是这种情况,请使用 ob_start 输出缓冲:

<?php
  ob_start();

  // code 

 ob_end_flush();
?> 

这将打开输出缓冲,并在页面缓冲后创建标题。

评论

23赞 Ja͢ck 5/15/2013
ob_start()只是隐藏了问题;不要用它来解决这个特定的问题。
7赞 Tomas 10/23/2015
ob_start()它不是“隐藏”问题,而是解决问题
1赞 GGSoft 12/9/2015
当我将文件上传到服务器时,我遇到了这样的问题,它甚至支持 PHP5.3 使用具有 PHP 5.6 或更高版本的服务器
1赞 Manish Shrivastava 5/6/2017
@jack真的,我同意你的看法。理想的方法是在开始 php 标签之前删除空格<?
2赞 NDi 1/26/2021
解决了我的问题。
100赞 Ipsita Rout 3/24/2013 #12

而不是下面的行

//header("Location:".ADMIN_URL."/index.php");

echo "<script>location.href = '".ADMIN_URL."/index.php?msg=$msg';</script>";

?><script><?php echo "location.href = '".ADMIN_URL."/index.php?msg=$msg';";?></script><?php

它一定会解决你的问题。 我遇到了同样的问题,但我通过以上述方式编写标题位置解决了。

23赞 MD. Sahib Bin Mahboob 11/8/2013 #13

另一个不好的做法可能会引发这个问题,但这个问题还没有说明。

请参阅以下代码片段:

<?php
include('a_important_file.php'); //really really really bad practise
header("Location:A location");
?>

一切都还好,对吧?

如果“a_important_file.php”是这样的:

<?php
//some php code 
//another line of php code
//no line above is generating any output
?>

 ----------This is the end of the an_important_file-------------------

这行不通吗?为什么?因为已经生成了新行。

现在,虽然这不是一个常见的场景,但如果你使用的是MVC框架,它在将东西移交给你的控制器之前加载了大量文件,该怎么办?这种情况并不少见。为此做好准备。

PSR-2 2.2 开始:


  • 所有 PHP 文件都必须使用 Unix LF(换行符)行尾
  • 所有 PHP 文件必须以一个空行结尾。
  • 仅包含 php 的文件必须省略结束 ?> 标记

相信我,遵循这些标准可以为您节省大量时间:)

评论

3赞 Daniel W. 4/4/2014
根据几个标准(例如Zend),在任何情况下都不应该以任何方式将结束标记放在任何文件中?>
0赞 Junior Mayhé 6/10/2015
我无法在 Windows 环境中重现它,因为它使用任何组合(添加结束标签、空白、按回车键等)工作。似乎这个问题主要发生在 Linux 环境中。
0赞 MD. Sahib Bin Mahboob 6/11/2015
@JuniorM 它应该是可重复的。你能分享一下你在要点或类似的东西中试验的代码吗?
0赞 Junior Mayhé 6/11/2015
我使用的是 Windows 7,安装了最新的 Wamp。我认为这个错误与行尾的隐藏字符有关。我的Wordpress的简码.php是导致问题的原因。我在这个文件中添加了一个简单的函数,它开始触发这个“标头已发送”错误。我已经将我的短代码 .php 与 wordpress' 进行了比较,还可以,除了(典型的 Windows 行尾)。我通过从 Wordpress 存储库下载原始文件来解决它,该文件具有(Linux 行尾)而不是,并且我还将我的函数移动到主题的函数 .php。基于: bit.ly/1Gh6mzNCR LFLFCR LF
0赞 Junior Mayhé 6/11/2015
@Sahib,请注意,我仍然无法重现此答案中所述的内容。对于Linux环境来说,答案是完全可以的。我已经测试了诸如空白之间,删除和添加单个空行,添加和省略结束标记之类的东西。在 Windows+ Wamp 中,所有这些组合都可以正常工作。Wierd...?><?php?>
4赞 2 revs, 2 users 96%Biswadeep Sarkar #14

通常,当我们在回显或打印后发送标头时,会出现此错误。如果此错误出现在特定页面上,请确保该页面没有回显任何内容,然后再调用 。start_session()

不可预知的错误示例:

 <?php //a white-space before <?php also send for output and arise error
session_start();
session_regenerate_id();

//your page content

再举一个例子:

<?php
includes 'functions.php';
?> <!-- This new line will also arise error -->
<?php
session_start();
session_regenerate_id();

//your page content

结论:在调用或函数之前不要输出任何字符,甚至不要输出空格或换行符session_start()header()

39赞 6 revs, 5 users 66%T.Todua #15

常见问题:

1) header(.......); 命令之前不应有任何输出(即 echo.. 或 HTML 代码)。

2)删除标签前后的任何空格(或换行符)。<?php?>

3黄金法则! - 检查该 php 文件(以及其他文件)是否具有没有 BOM 编码的 UTF8(而不仅仅是 UTF-8)。这在许多情况下都是有问题的(因为 UTF8 编码的文件在 php 文件的开头有一些特殊字符,您的文本编辑器不会显示)!!!!!!!!!!include

4)必须使用后header(...);exit;

5) 始终使用 301 或 302 参考:

header("location: http://example.com",  true,  301 );  exit;

6) 打开错误报告,并查找错误。您的错误可能是由功能不起作用引起的。打开错误报告时,应始终首先修复最上面的错误。例如,它可能是“警告:date_default_timezone_get():依赖系统的时区设置是不安全的”——然后再往下看,您可能会看到“标头未发送”错误。修复最顶层(第1个)错误后,重新加载页面。如果仍有错误,请再次修复最上面的错误。

7)如果以上方法均无济于事,请使用JAVSCRIPT重定向(但是,强烈建议不要使用该方法),在自定义情况下可能是最后的机会...:

echo "<script type='text/javascript'>window.top.location='http://website.com/';</script>"; exit;

评论

0赞 Jānis Elmeris 4/15/2019
为什么显式设置或重要?301302
0赞 7/10/2020
如果设置了 php.ini 文件中的“output_buffering”,则可以输出。我在我家的 Debian 系统上设置为 4096。我正在使用的服务器上的那个声明它没有设置。
16赞 2 revs, 2 users 91%Lupin #16

有时,当开发过程同时具有 WIN 工作站和 LINUX 系统(托管)并且在代码中看不到相关行之前的任何输出时,这可能是文件的格式化和缺少 Unix LF(换行)行尾。

为了快速解决这个问题,我们通常做的是重命名文件,然后在 LINUX 系统上创建一个新文件而不是重命名的文件,然后将内容复制到其中。很多时候,这解决了这个问题,因为在WIN中创建的某些文件一旦移动到主机就会导致此问题。

对于我们通过FTP管理的站点来说,此修复程序是一个简单的修复程序,有时可以为我们的新团队成员节省一些时间。