如何修复 PHP 中的“标头已发送”错误
Posted
技术标签:
【中文标题】如何修复 PHP 中的“标头已发送”错误【英文标题】:How to fix "Headers already sent" error in PHP 【发布时间】:2011-12-23 03:48:09 【问题描述】:在运行我的脚本时,我遇到了几个类似这样的错误:
警告:无法修改标头信息 - 标头已由 /some/file.php 中的(输出开始于 /some/file.php:12)发送strong>第 23 行
错误消息中提到的行包含header()
和setcookie()
调用。
这可能是什么原因?以及如何解决?
【问题讨论】:
阅读:***.com/questions/1912029/… 确保不输出任何文本(ob_start
和 ob_end_clean()
在这里可能很有用)。然后您可以设置一个等于ob_get_contents()
的cookie 或会话,然后使用ob_end_clean()
清除缓冲区。
在我的 php 库中使用 safeRedirect
函数:github.com/heinkasner/PHP-Library/blob/master/extra.php
~~~~~~~~~~ 你的文件编码不应该是UTF-8
,而是UTF-8 (Without BOM)
~~~~~~~~~~~
【参考方案1】:
发送标头前无输出!
必须调用发送/修改 HTTP 标头的函数在进行任何输出之前。 summary ⇊ 否则调用失败:
警告:无法修改标头信息 - 标头已发送(输出开始于 script:line)
一些修改HTTP头的函数有:
header
/ header_remove
session_start
/ session_regenerate_id
setcookie
/ setrawcookie
输出可以是:
无意:
<?php
之前或?>
之后的空格
UTF-8 Byte Order Mark 专门
以前的错误消息或通知
故意的:
print
、echo
和其他产生输出的函数
<html>
之前的原始 <?php
代码部分。
为什么会这样?
要了解为什么必须在输出之前发送标头,有必要 看一个典型的HTTP 回复。 PHP 脚本主要生成 HTML 内容,同时也传递一个 一组 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 必须通过 首先将标头发送到网络服务器。它只能这样做一次。 双换行后再也不能修改了。
当 PHP 接收到第一个输出(print
、echo
、<html>
)时,它将
flush 所有收集的标头。之后它可以发送所有输出
它想要。但是,发送更多的 HTTP 标头是不可能的。
如何找出过早输出发生的位置?
header()
警告包含所有相关信息
定位问题原因:
警告:无法修改标头信息 - 标头已由 (输出开始于 /www/usr2345/htdocs/auth.php:52)在 /www/usr2345/htdocs/index.php 第 100 行
这里的“第 100 行”指的是 header()
调用失败的脚本。
括号内的“输出开始于”注释更重要。
它指定先前输出的来源。在这个例子中,它是auth.php
和线52
。这就是你必须寻找过早输出的地方。
典型原因:
打印,回显
print
和 echo
语句的有意输出将终止发送 HTTP 标头的机会。必须重组应用程序流程以避免这种情况。使用functions
和模板方案。确保header()
调用发生在之前 消息
都写出来了。
产生输出的函数包括
print
, echo
, printf
, vprintf
trigger_error
, ob_flush
, ob_end_flush
, var_dump
, print_r
readfile
, passthru
, flush
, imagepng
, imagejpeg
以及用户定义的函数。
原始 HTML 区域
.php
文件中未解析的 HTML 部分也是直接输出。
必须注意将触发header()
调用的脚本条件
在任何原始<html>
块之前。
<!DOCTYPE html>
<?php
// Too late for headers already.
使用模板方案将处理与输出逻辑分开。
将表单处理代码置于脚本之上。 使用临时字符串变量来延迟消息。 实际的输出逻辑和混合的 HTML 输出应该在最后。
<?php
之前的空格用于“script.php line 1”警告
如果警告是指输出内联1
,那么它主要是
在开头的<?php
标记前前导空格、文本或HTML。
<?php
# There's a SINGLE space/newline before <? - Which already seals it.
附加的脚本或脚本部分也可能发生这种情况:
?>
<?php
PHP 实际上在关闭标签之后吃掉了一个 single 换行符。但它不会 补偿移入此类间隙的多个换行符或制表符或空格。
UTF-8 物料清单
单独的换行符和空格可能是个问题。但也有“隐形”
可能导致这种情况的字符序列。最著名的是
UTF-8 BOM (Byte-Order-Mark)
大多数文本编辑器不显示。它是字节序列EF BB BF
,对于 UTF-8 编码的文档是可选的和冗余的。然而,PHP 必须将其视为原始输出。它可能在输出中显示为字符 
(如果客户端将文档解释为 Latin-1)或类似的“垃圾”。
尤其是图形编辑器和基于 Java 的 IDE 在场。他们没有将其可视化(Unicode 标准要求)。 然而,大多数程序员和控制台编辑器都会这样做:
很容易尽早发现问题。其他编辑可能会识别
它出现在文件/设置菜单中(Windows 上的 Notepad++ 可以识别和
remedy the problem),
检查 BOM 存在的另一种选择是使用 hexeditor。
在 *nix 系统上hexdump
通常可用,
如果不是简化审核这些问题和其他问题的图形变体:
一个简单的解决方法是将文本编辑器设置为将文件保存为“UTF-8(无 BOM)” 或类似这样的命名法。新手通常会采取其他方式创建新文件,然后将以前的代码复制并粘贴回去。
更正实用程序
还有用于检查和重写文本文件的自动化工具
(sed
/awk
或 recode
)。
对于 PHP,特别是有 phptags
tag tidier。
它将关闭和打开标签重写为长格式和短格式,但也很容易
修复前导和尾随空格、Unicode 和 UTF-x BOM 问题:
phptags --whitespace *.php
在整个包含或项目目录中使用是安全的。
?>
后面的空格
如果错误源在后面提到
closing ?>
然后这是一些空白或原始文本被写出的地方。
PHP 结束标记此时不会终止脚本执行。其后的任何文本/空格字符都将作为页面内容写出
还是。
通常建议,特别是对于新手,尾随 ?>
PHP
应省略关闭标签。这避开这些案例的一小部分。
(通常include()d
脚本是罪魁祸首。)
提到的错误源为“第 0 行未知”
如果没有错误源,通常是 PHP 扩展或 php.ini 设置 被具体化了。
偶尔是gzip
流编码设置
or the ob_gzhandler
。
但它也可以是任何双重加载的extension=
模块
生成隐式 PHP 启动/警告消息。
前面的错误消息
如果另一个 PHP 语句或表达式导致警告消息或 注意被打印出来,也算过早输出。
在这种情况下,您需要避免错误,
延迟语句执行,或使用例如抑制消息
isset()
或 @()
-
当任何一个都不会妨碍以后的调试时。
没有错误信息
如果您根据php.ini
禁用了error_reporting
或display_errors
,
然后不会出现任何警告。但是忽略错误不会解决问题
离开。过早输出后仍然无法发送标头。
所以当header("Location: ...")
重定向静默失败时,它非常
建议探测警告。使用两个简单的命令重新启用它们
在调用脚本之上:
error_reporting(E_ALL);
ini_set("display_errors", 1);
或者set_error_handler("var_dump");
,如果一切都失败了。
说到重定向标头,您应该经常使用类似的成语 这是最终代码路径:
exit(header("Location: /finished.html"));
最好是实用功能,打印用户消息
在header()
失败的情况下。
输出缓冲作为一种解决方法
PHP output buffering 是缓解此问题的一种解决方法。它通常可靠地工作,但不应该 替代适当的应用程序结构并将输出与控制分离 逻辑。它的实际目的是尽量减少到网络服务器的分块传输。
output_buffering=
设置仍然可以提供帮助。
配置在php.ini
或通过.htaccess
甚至.user.ini on
现代 FPM/FastCGI 设置。
启用它将允许 PHP 缓冲输出,而不是立即将其传递给网络服务器。因此 PHP 可以聚合 HTTP 标头。
同样可以拨打ob_start();
在调用脚本之上。然而,由于多种原因,这不太可靠:
即使<?php ob_start(); ?>
启动第一个脚本,空格或
BOM 之前可能会被洗牌,rendering it ineffective。
它可以隐藏 HTML 输出的空白。但是一旦应用程序逻辑尝试发送二进制内容(例如生成的图像),
缓冲的无关输出成为问题。 (需要ob_clean()
作为进一步的解决方法。)
缓冲区的大小是有限的,并且在保留默认值时很容易溢出。 这也不是罕见的事情,difficult to track down 当它发生时。
因此,这两种方法都可能变得不可靠——尤其是在两者之间切换时 开发设置和/或生产服务器。这就是为什么输出缓冲是 被广泛认为只是一种拐杖/严格来说是一种解决方法。
另见basic usage example 在手册中,以及更多的利弊:
What is output buffering? Why use output buffering in PHP? Is using output buffering considered a bad practice? Use case for output buffering as the correct solution to "headers already sent"但它在另一台服务器上工作!?
如果您之前没有收到标题警告,那么output buffering php.ini setting 已经改变。它可能在当前/新服务器上未配置。
检查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 redirect 可用于页面重定向:
<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 设置等)
更多链接
Google 提供了lengthy list of similar discussions。 当然,many specific cases 也被 Stack Overflow 报道过。 WordPress 常见问题解答以通用方式解释了 How do I solve the Headers already sent warning problem?。 Adobe 社区:PHP development: why redirects don't work (headers already sent) Nucleus 常见问题解答:What does "page headers already sent" mean? 更详尽的解释之一是HTTP Headers and the PHP header() Function - A tutorial by NicholasSolutions(Internet 存档链接)。 它详细介绍了 HTTP,并提供了一些重写脚本的指南。【讨论】:
常规 notepad.exe 也很棘手。我通常使用不添加 BOM 的 NetBeans,即使文件是这样编码的。稍后在记事本中编辑文件会使事情变得混乱,尤其是将 IIS 作为网络服务器。似乎 apache 丢弃了(无意添加的)BOM。 从 php 文件的末尾删除关闭?>
通常是一种很好的做法,它也有助于最大限度地减少这些错误。不需要的空格不会出现在文件的末尾,您仍然可以稍后将标头添加到响应中。如果您使用输出缓冲,它也很方便,并且不希望在包含的文件生成的部分末尾看到添加的不需要的空白。
奇怪的事情,我把我的文件从 cPanel Linux Hosting 移到了 VPS。在它正常工作之前,但在这里它显示了这个错误。(我在标题之前有一些 html 代码)。为什么?
@PeterSMcIntyre UTF8 BOM 大概(修复)/没有启用输出缓冲(不要依赖它)。
只是遗漏了一个细节,您可能需要添加。如果错误消息省略了该信息,$file = $line = null; headers_sent($file, $line); die("$file:$line");
将准确告诉您发送标头的内容。不知道为什么,但有时似乎是这样。【参考方案2】:
我之前多次遇到过这个错误,我敢肯定所有 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();
?>
这将打开输出缓冲,并在页面缓冲后创建标题。
【讨论】:
ob_start()
只是隐藏了问题;不要用它来解决这个特定的问题。
ob_start()
没有“隐藏”问题,它解决了问题。
我上传文件到服务器时遇到了这样的问题,它甚至支持 PHP5.3 使用 PHP 5.6 或更高版本的服务器
@jack 是的,我同意你的看法。理想的方法是在开始php标签<?
之前删除空格
解决了我的问题。【参考方案3】:
常见问题:
(复制自:source)
=====================
1)在header(.......);
命令之前不应有任何输出(即echo..
或HTML代码)。 p>
2) 删除<?php
之前和?>
标记之后的所有空白(或换行符)。
3) 黄金法则! - 检查该 php 文件(以及,如果你 include
其他文件)是否有 UTF8 没有 BOM 编码(而不仅仅是 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;
【讨论】:
为什么显式设置301
或302
很重要?
如果设置了 php.ini 文件中的“output_buffering”,就可以有输出。我在我家的 Debian 系统上设置为 4096。我使用的服务器上的那个说它没有设置。【参考方案4】:
有时,当开发进程同时具有 WIN 工作站和 LINUX 系统(托管)并且在代码中您在相关行之前看不到任何输出时,可能是文件的格式和缺少 Unix LF(换行) 行结束。
为了快速解决这个问题,我们通常会做的是重命名文件并在 LINUX 系统上创建一个新文件而不是重命名的文件,然后将内容复制到该文件中。很多时候这解决了这个问题,因为在 WIN 中创建的一些文件一旦移动到主机就会导致这个问题。
对于我们通过 FTP 管理的网站来说,此修复是一个简单的修复,有时可以为我们的新团队成员节省一些时间。
【讨论】:
【参考方案5】:通常当我们在回显或打印后发送标题时会出现此错误。如果此错误出现在特定页面上,请确保该页面没有回显任何内容,然后再调用 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()
函数之前不要输出任何字符,即使是空格或换行符也不行
【讨论】:
【参考方案6】:另一个不好的做法可能会引发这个尚未说明的问题。
查看这段代码sn-p:
<?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
的文件
相信我,遵循这些标准可以为您节省大量时间:)
【讨论】:
根据几个标准(例如 Zend),在任何情况下,您都不应该将结束?>
标记放在任何文件中
我无法在 Windows 环境中重现它,因为它可以使用任何组合(添加结束标签、空格、按 Enter 键等)。似乎这个问题主要发生在 Linux 环境中。
@JuniorM 它应该是可重现的。你能分享一下你在 gist 或类似的东西上试验的代码吗?
我在 Windows 7 上,安装了最新的 Wamp。我认为这个错误与行尾的隐藏字符有关。我的 Wordpress 的 shortcodes.php 是导致问题的原因。我在这个文件中添加了一个简单的函数,它开始触发这个“headers sent”错误。我已经将我的 shortcodes.php 与 wordpress' 进行了比较,除了 CR LF
(典型的 Windows 行尾)之外,一切正常。我通过从具有LF
(Linux 行尾)而不是CR LF
的Wordpress repo 下载原始文件来解决它,并且我还将我的函数移动到主题的functions.php。基于:bit.ly/1Gh6mzN
@Sahib,请注意我仍然无法重现此答案中所述的内容。答案对于 Linux 环境来说完全没问题。我已经测试了?>
<?php
之间的空白,删除和添加单个空白行,添加和省略结束标记?>
。在 Windows+ Wamp 中,所有这些组合都可以正常工作。奇怪...【参考方案7】:
而不是下面的行
//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
它肯定会解决您的问题。 我遇到了同样的问题,但我通过上面的方式写标题位置解决了。
【讨论】:
【参考方案8】:一个简单的提示:脚本中的一个简单空格(或不可见的特殊字符),就在第一个 <?php
标记之前,可能会导致这种情况!
尤其是当您在团队中工作并且有人使用“弱”的 IDE 或使用奇怪的文本编辑器处理文件时。
我见过这些东西;)
【讨论】:
【参考方案9】:如果在您发送 HTTP 标头(使用 setcookie
或 header
)之前发送 anything,则会触发此错误消息。在 HTTP 标头之前输出内容的常见原因是:
意外的空格,通常在文件的开头或结尾,如下所示:
<?php
// Note the space before "<?php"
?>
为避免这种情况,只需省略结尾的?>
- 无论如何都不需要。
3F 3C
开头。您可以安全地从文件开头删除 BOM EF BB BF
。
显式输出,例如调用echo
、printf
、readfile
、passthru
、<?
之前的代码等。
如果设置了display_errors
php.ini 属性,则php 输出警告。 php 不会因为程序员的错误而崩溃,而是默默地修复错误并发出警告。虽然您可以修改 display_errors
或 error_reporting 配置,但您应该解决问题。
常见原因是访问数组的未定义元素(例如 $_POST['input']
未使用 empty
或 isset
来测试是否设置了输入),或使用未定义常量而不是字符串文字(如 $_POST[input]
,请注意缺少的引号)。
打开output buffering 应该可以解决问题;调用ob_start
后的所有输出都缓存在内存中,直到您释放缓冲区,例如ob_end_flush
。
但是,虽然输出缓冲可以避免这些问题,但您应该真正确定应用程序在 HTTP 标头之前输出 HTTP 正文的原因。这就像在告诉来电者他打错号码之前,先打个电话讨论一下你的日子和天气。
【讨论】:
【参考方案10】:正是因为这一行:
printf ("Hi %s,</br />", $name);
在发送标头之前,您不应打印/回显任何内容。
【讨论】:
【参考方案11】:你会的
printf ("Hi %s,</br />", $name);
在设置 cookie 之前,这是不允许的。您不能在标题之前发送任何输出,甚至是空行。
【讨论】:
以上是关于如何修复 PHP 中的“标头已发送”错误的主要内容,如果未能解决你的问题,请参考以下文章