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


831

运行脚本时,出现如下错误:

警告:不能更改头信息-已经(发送了头输出在/some/file.php:12开始)在/some/file.php线23

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

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



确保没有文本输出(ob_startob_end_clean() 可能证明是有用的在这里)。然后,您可以将cookie或会话设置为等于ob_get_contents(),然后用于ob_end_clean()清除缓冲区。
杰克塔克2014年

使用safeRedirect我的PHP库中的函数:github.com/heinkasner/PHP-Library/blob/master/extra.php
heinkasner 2014年

5
~~~~~~~~~~~您的文件编码不应该是UTF-8,而是UTF-8 (Without BOM)~~~~~~~~~~~~
T.Todua 2014年

Answers:


2994

发送头之前无输出!

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

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

修改HTTP标头的一些功能是:

输出可以是:

  • 故意的:

    • printecho以及产生输出的其他函数
    • 原始<html>节的先前<?php代码。

为什么会发生?

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

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接收第一输出(printecho<html>),它会 刷新所有收集的头。之后,它可以发送所需的所有输出。但是,那时不可能发送更多的HTTP标头。

您如何找出过早输出发生的位置?

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

警告:无法修改标头信息- 行100上/www/usr2345/htdocs/index.php中已经发送过的标头 (输出始于 / www / usr2345 / htdocs / auth.php:52

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

括号内的“ 输出始于 ”注释更为重要。它表示先前输出的来源。在此示例中,为auth.phpline52。那是您必须寻找过早输出的地方。

典型原因:

  1. 打印,回显

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

    产生输出的功能包括

    • printechoprintfvprintf
    • trigger_errorob_flushob_end_flushvar_dumpprint_r
    • readfilepassthruflushimagepngimagejpeg


    以及用户定义的功能。

  2. 原始HTML区域

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

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

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

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

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

    如果警告指的是line中的输出1,则在打开令牌之前,它主要是前导空格,文本或HTML <?php

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

    类似地,它可能发生在附加的脚本或脚本节中:

    ?>
    
    <?php

    PHP实际上吃了一个单一的关闭标签后断行。但这不会补偿多个换行符或制表符或空格之间的空白。

  4. UTF-8 BOM

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

    特别是图形编辑器和基于Java的IDE忽略了它的存在。他们没有可视化它(受Unicode标准约束)。但是,大多数程序员和控制台编辑器都这样做:

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

    尽早发现问题很容易。其他编辑器可以在文件/设置菜单中识别其存在(Windows上的Notepad ++可以识别和 纠正问题)。检查BOMs存在的另一种方法是使用hexeditorhexdump通常在* nix系统上可用,如果没有图形版本,则可以简化审核这些问题和其他问题:

    beav hexeditor显示utf-8 bom

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

    校正工具

    还有自动工具可以检查和重写文本文件(sed/awkrecode)。专门针对PHP,有phptags标记tidier。它将关闭和打开的标签改写为长号和短号形式,而且还可以轻松修复开头和结尾的空格,Unicode和UTF-x BOM问题:

    phptags  --whitespace  *.php

    在整个包含目录或项目目录上使用都是理智的。

  5. 之后的空白 ?>

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

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

  6. 错误源提到为“第0行未知”

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

    • 有时是gzip流编码设置 ob_gzhandler
    • 但是也可能是任何双重加载的extension=模块生成隐式的PHP启动/警告消息。

  7. 前面的错误信息

    如果另一个PHP语句或表达式导致打印出警告消息或通知,则也将视为过早输出。

    在这种情况下,您需要避开错误,延迟语句的执行,或者使用例如isset()@()- 抑制消息, 但以后任何一个都不会妨碍调试。

没有错误讯息

如果您已根据设置error_reportingdisplay_errors禁用php.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= 设置仍然可以提供帮助。在现代FPM / FastCGI设置中, 通过php.ini.htaccess 甚至.user.ini对其进行配置。
    启用它将允许PHP缓冲输出,而不是立即将其传递到Web服务器。因此,PHP可以聚合HTTP标头。

  2. 它也可以与ob_start(); 调用脚本顶部的调用一起使用。但是,由于多种原因,它的可靠性较差:

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

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

    • 缓冲区的大小是有限的,当保留默认值时,缓冲区很容易溢出。而且这也不是少见的 事情,发生时很难追踪

因此,两种方法都可能变得不可靠-特别是在开发设置和/或生产服务器之间切换时。这就是为什么输出缓冲被广泛视为拐杖/严格来说是一种解决方法的原因。

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

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

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

与检查 headers_sent()

您始终可以headers_sent()用来探测是否仍然可以...发送标头。这对于有条件地打印信息或应用其他后备逻辑很有用。

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">

    当在本<head>节中使用时,这将导致无效的HTML 。大多数浏览器仍然接受它。

  • JavaScript重定向

    或者, 可以将JavaScript重定向用于页面重定向:

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

    尽管这通常比<meta>解决方法更符合HTML要求,但它会依赖具有JavaScript功能的客户端。

但是,当真正的HTTP header()调用失败时,两种方法都会产生可接受的回退。理想情况下,您总是将其与用户友好的消息和可点击的链接结合起来作为最后的选择。(例如,http_redirect() PECL扩展名就是这样做的。)

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

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

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

其他连结


常规的notepad.exe也很棘手。我通常使用不添加BOM的NetBeans,即使文件经过编码也是如此。稍后在记事本中编辑文件会使事情搞砸,尤其是将IIS作为Web服务器。似乎apache丢弃了(有意添加的)BOM。
2015年

4
?>通常,从php文件末尾删除结束符是一种很好的做法,它有助于最大程度地减少这些错误。不需要的空格不会出现在文件末尾,以后您仍然可以在响应中添加标头。如果使用输出缓冲,并且不希望在包含文件生成的部分的末尾看到多余的空格,这也很方便。
Nikita

奇怪的是,我将文件从cPanel Linux Hosting移到了VPS。在它正常工作之前,但是这里显示了这个错误。(我在标头之前有一些html代码)。为什么?
Pablo Escobar

@Purushotamrawat您是否阅读过有关“ 但它在另一台服务器上有效! ”的部分
mario

1
@PeterSMcIntyre UTF8 BOM大概(已修复)/未启用输出缓冲(不依赖于此)。
mario'8

199

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

  • 偶然的空格,通常在文件的开头或结尾,如下所示:

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

       为了避免这种情况,只需省略结尾处?>-仍然不需要。

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

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

但是,尽管输出缓冲可以避免这些问题,但您应该真正确定为什么应用程序在HTTP标头之前输出HTTP正文。这就好比打个电话,讨论您的一天和天气,然后告诉呼叫者电话号码错误。


它帮助我,谢谢
Vishwa Pratap

122

我之前曾多次收到此错误,并且可以肯定所有PHP程序员至少一次收到过此错误。

可能的解决方案1

此错误可能是由文件开头之前或文件结尾之后的空格引起的。这些空格不应在此处。

例)这里应该没有空白

   echo "your code here";

?>
THERE SHOULD BE NO BLANK SPACES HERE

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

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

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

<?php
  ob_start();

  // code 

 ob_end_flush();
?> 

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


18
ob_start()只是隐藏了问题;不要用它来解决这个特定的问题。
杰克

@Ja͢ck如果我不使用ob_start(),那么该如何解决此问题:Headers already sent
Shafizadeh 2015年

@Sajad如果由于使用的编辑器而导致错误,则应摆弄设置以使其停止引起问题,或切换编辑器。如果由于任何其他原因导致错误,则应通读此问题中的答复(特别是已接受的答案),以找出问题的根源并加以解决。
Samsquanch,2015年

3
ob_start()不会“隐藏”问题,而是可以解决问题。
TMS

1
我将文件上传到服务器时遇到了这样的问题,该服务器甚至支持PHP5.3使用PHP 5.6或更高版本的服务器
GGSoft

86

代替下面的行

//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

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



32

因为这条线:

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

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


31

常见问题:

(复制自:

====================

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

2)除去任何空白(或换行之前)<?php和后?>标记。

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

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

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

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

6) 打开错误报告,然后找到错误。 您的错误可能是由无法使用的功能引起的。开启错误报告功能时,应始终首先修复最上面的错误。例如,可能为“警告:date_default_timezone_get():依靠系统的时区设置并不安全。” -再往下走,您可能会看到“未发送标题”错误。修复最上面的(第一个)错误后,请重新加载页面。如果仍然有错误,请再次修复最上面的错误。

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

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

为什么要明确设置301302重要?
贾尼斯·埃默里斯(JānisElmeris)

26

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

我看过这些东西;)


22

另一个不好的做法可能会引起此问题,尚未说明。

请参见以下代码段:

<?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 (linefeed) line ending
  • 所有PHP文件都必须以结尾single blank line
  • 结束符?>标记必须omitted来自包含以下内容的文件only php

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


2
根据几种标准(例如Zend),?>无论如何您都不应将结束标记放在任何文件中
Daniel W.

我无法在Windows环境中重现此内容,因为它可以使用任意组合(添加结束标记,空格,按Enter键等)工作。看来这个问题主要发生在Linux环境中。
少年梅耶2015年

@JuniorM应该是可复制的。您能否共享要点或类似的实验代码?
MD。Sahib Bin Mahboob 2015年

我在Windows 7上,安装了最新的Wamp。我认为此错误与行尾的隐藏字符有关。我的Wordpress的shortcodes.php是导致此问题的原因。我向此文件添加了一个简单的功能,它开始触发此“已发送标题”错误。我已经将我的shortcodes.php与wordpress进行了比较,并且还可以,除了CR LF(典型的Windows行尾)。我通过从Wordpress存储库LF(而不是Linux行尾)下载原始文件来解决此问题,CR LF并且我还将函数移至主题的functions.php。基于:bit.ly/1Gh6mzN
少年梅耶2015年

@Sahib,请注意,我仍然无法复制此答案中的内容。对于Linux环境,答案是完全可以的。我已经测试了诸如在之间的空白?> <?php,删除和添加单个空白行,添加和省略了结束标记之类的东西?>。在Windows + Wamp中,所有这些组合都可以正常工作。Wierd ...
少年梅耶2015年

15

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

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

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


2

通常,当我们在回显或打印后发送标题时,会出现此错误。如果在特定页面上出现此错误,请确保在调用之前该页面没有回显任何内容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()功能甚至没有空格或新行

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.