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

2023-11-26

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

警告:无法修改标头信息 - 标头已由 (输出从 /some/file.php:12 开始) in /一些/文件.php on line 23

错误消息中提到的行包含header() and setcookie() calls.

这可能是什么原因?以及如何修复它?


Answer recommended by PHP Collective

发送标头之前没有输出!

Functions that send/modify HTTP headers must be invoked before any output is made. summary ⇊ Otherwise the call fails:

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

修改 HTTP 标头的一些函数是:

  • header / header_remove
  • session_start / session_regenerate_id
  • setcookie / setrawcookie

输出可以是:

  • 无意的:

    • 前面有空格<?php或之后?>
    • The UTF-8 字节顺序标记具体来说
    • 以前的错误消息或通知
  • 故意的:

    • print, echo和其他产生输出的函数
    • Raw <html>之前的章节<?php code.

为什么会发生这种情况?

要理解为什么必须在输出之前发送标头,这是必要的 看看一个典型的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>

页面/输出始终follows标题。 PHP 必须通过 首先将标头发送到网络服务器。它只能这样做一次。 双换行之后就再也不能修改它们了。

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

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

The header()警告包含所有相关信息 定位问题原因:

警告:无法修改标头信息 - 标头已发送(输出开始于/www/usr2345/htdocs/auth.php:52) 在 /www/usr2345/htdocs/index.php 第 100 行

这里的“第 100 行”指的是脚本header() 调用 failed.

The "输出开始于括号内的注释更有意义。 它表示先前输出的来源。在这个例子中,它是auth.php and line 52。这就是你必须寻找过早输出的地方。

典型原因:

  1. 打印、回显

    有意输出print and echo语句将终止发送 HTTP 标头的机会。必须重组应用程序流程以避免这种情况。使用功能和模板方案。确保header()发生呼叫before消息 都写出来了。

    产生输出的函数包括

    • print, echo, printf, vprintf
    • trigger_error, ob_flush, ob_end_flush, var_dump, print_r
    • readfile, passthru, flush, imagepng, imagejpeg


    其中以及用户定义的函数。

  2. 原始 HTML 区域

    中未解析的 HTML 部分.php文件也是直接输出。 将触发的脚本条件header()来电必须注明 前any raw <html> blocks.

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

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

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

  3. 前面有空格<?php对于“脚本.phpline 1” 警告

    如果警告涉及内联输出1,那么主要是 领导空白、开头之前的文本或 HTML<?php token.

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

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

    ?>
    
    <?php
    

    PHP实际上吃掉了一个single关闭标签后换行。但它不会 补偿移入此类间隙的多个换行符或制表符或空格。

  4. UTF-8 BOM

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

    特别是图形编辑器和基于 Java 的 IDE 忽略了它 在场。他们没有将其可视化(Unicode 标准规定)。 然而,大多数程序员和控制台编辑器都会:

    joes editor showing UTF-8 BOM placeholder, and MC editor a dot

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

    beav hexeditor showing utf-8 bom

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

    Correction utilities

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

    phptags  --whitespace  *.php
    

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

  5. 后面有空格?>

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

    人们普遍建议,特别是对于新手来说,尾随?>PHP 应省略关闭标签。这eschews这些案例中的一小部分。 (很常见include()d脚本是罪魁祸首。)

  6. 错误源提到为“Unknown on line 0”

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

    • 有时候是这样的gzip码流编码设置or the ob_gzhandler.
    • 但它也可以是任何双负载的extension=模块 生成隐式 PHP 启动/警告消息。

  7. 前面的错误消息

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

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

无错误信息

如果你有error_reporting or display_errors禁用每php.ini, 那么就不会出现任何警告。但忽略错误并不能解决问题 离开。过早输出后仍然无法发送标头。

So when header("Location: ...")重定向默默地失败了,这非常 建议探测警告。使用两个简单的命令重新启用它们 在调用脚本之上:

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

Or set_error_handler("var_dump");如果一切都失败了。

说到重定向标头,您应该经常使用这样的习惯用法 这对于最终的代码路径:

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

最好是一个打印用户消息的实用函数 的情况下header()失败。

输出缓冲作为解决方法

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

  1. The output_buffering=不过,设置还是有帮助的。 将其配置在php.ini or via .htaccess甚至.user.ini在 现代 FPM/FastCGI 设置。
    启用它将允许 PHP 缓冲输出,而不是立即将其传递到网络服务器。 PHP 因此可以聚合 HTTP 标头。

  2. 它也可以通过调用来参与ob_start();在调用脚本之上。然而,由于多种原因,该方法不太可靠:

    • Even if <?php ob_start(); ?>开始第一个脚本、空格或 BOM之前可能会被洗牌,使其无效.

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

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

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

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

  • PHP 中的输出缓冲是什么?
  • 为什么在 PHP 中使用输出缓冲?
  • 使用输出缓冲是否被认为是一种不好的做法?
  • 输出缓冲的用例作为“标头已发送”的正确解决方案

但在另一台服务器上却能正常工作!?

如果您之前没有收到标题警告,那么输出缓冲 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> tag

    如果您的应用程序在结构上很难修复,那么一个简单的(但 有点不专业)允许重定向的方法是注入 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>
    

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

然而,当真正的 HTTP header() 时,这两种方法都可以接受 呼叫失败。理想情况下,您总是将其与用户友好的消息结合起来, 作为最后手段的可点击链接。 (例如,这就是http_重定向()PECL 扩展可以。)

Why setcookie() and session_start()也受到影响

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

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

更多链接

  • 谷歌提供了一个类似讨论的冗长列表.
  • 而且当然许多具体案例Stack Overflow 上也有介绍。
  • WordPress 常见问题解释如何解决标头已发送警告问题?以一般的方式。
  • Adobe 社区:PHP 开发:为什么重定向不起作用(标头已发送)
  • 核常见问题解答:“页眉已发送”是什么意思?
  • 更彻底的解释之一是HTTP 标头和 PHP header() 函数 - NicholasSolutions 提供的教程(互联网档案链接)。 它详细介绍了 HTTP,并提供了一些重写脚本的指南。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐

  • MongoDB 区分 undefined 和 null

    我正在检查查询非值的逻辑 并在使用时注意到mongoshell 它区分undefined and null values gt use test gt db test insert a 1 b null c undefined gt db
  • HQL 加入 - 加入所需的路径!休眠

    我是休眠新手 遇到了以下问题 当我尝试运行此查询时 出现 连接所需的路径 异常 String hql select avg t price from Ticket t JOIN Flight f WHERE f number flightN
  • 如何增加 MS Access 2007 数据库大小?

    我开发了一个Windows应用程序 后端数据库是Access 2007 我听说Access 2007的最大限制是2GB 现在我的问题是 有什么办法可以将大小增加到超出该限制吗 如何为应用程序创建多个数据库以增加大小和性能 您可以将数据分区到
  • 在 .js 文件中调用 URL 参数

    我正在 HTML 文件中调用 js 文件 在 js 文件的 URL 上 我想包含一个可供 js 文件内的代码访问的参数 例如 我希望能够在 jQuery 的帮助下将 ID 值传递给 jquery widget js 文件中的函数 这是怎么做
  • Groovy 地图点键嵌套地图

    我有一个带有点表示法键的地图 但我需要它作为嵌套地图 test key one value1 text key two value2 现在结果应该是 test key one value1 two value2 这是我的代码想法 def e
  • C# ListView 项目图像

    如何使用 foreach 语句将图像 指定图像 添加到 listview 中 例如 foreach Video entry in videoFeed Entries listview1 items add entry listview1 i
  • Sprite Kit 中可重用的多线程实现

    我正在开发 Sprite Kit 游戏 我需要做一些多线程来保持健康的 fps 更新时 我调用一个函数来创建大量 UIBezierPaths 并使用 C 静态库合并它们 如果我有超过 10 个形状 帧速率会急剧下降 因此我决定尝试一下 GC
  • 更好的地图构造器

    有没有更简化的方法来执行以下操作 Map
  • 我在哪里可以了解经过验证的共享加密密钥的方法?

    假设一个群组想要加密一些信息 然后以需要群组共识的方式在群组成员之间共享加密密钥来解密该信息 我对各种场景感兴趣 其中共识的广度范围从一致到绝对多数 有用的技术可以应用于对称密钥 私钥或两者 我可以尝试推出自己的方法 我相信许多 SO 成员
  • KCFinder '您无权列出文件。'

    我在 ckeditor 中集成 KCFinder 时遇到问题 我的ckeditor版本是4 0 另一个 KCFinder版本 是2 52 dev 您好 像这样配置 ckeditor 的 config js CKEDITOR config b
  • 如何在Shapely中获得线上等距点

    我试图 大致 将一条线的点均匀地间隔到预定义的距离 距离之间有一定的公差是可以的 但最好尽可能接近 我知道我可以手动迭代线路中的每个点并检查 p1 与 p2 的距离 并根据需要添加更多点 但我想知道是否有人知道是否有办法用 shapely
  • 如何更改 matlab 颜色条缩放比例

    我很难理解如何更改 Matlab 2015b 中颜色条的范围 默认情况下 它的范围从 0 到 1 我设法使用以下方法更改标签 c colorbar c Limits 0 180 the range that I want 问题是当我这样做时
  • 在 CSS/SVG 中是否有一种普遍支持的方法来制作倾斜的“磨砂玻璃”效果?

    我正在寻找制作一个网站醒目页面 该页面将有一个背景 该背景将在左侧被一个倾斜的 div 截断 例如与水平方向保持 110 度 或同等程度 继续阅读 该 div 会模糊其背后的背景 并允许在其上放置内容 例如文本 请参阅YouTube 品牌资
  • OSGI OBR 存储库托管?

    有谁知道有哪些服务可以提供 OBR 来托管我自己的捆绑包 类似于 github 但用于捆绑包 如果没有 有人创建过自己的 OBR 服务器吗 这有多难 编辑 我找到了一个解决方案Nexus Pro 但对于凡人来说似乎太贵了 因为专业版的价格无
  • 在 javascript 中加密并使用 AES 算法在 C# 中解密

    我尝试使用 AES 库进行角度加密AES 我使用以下方法加密了字符串CryptoJS AES encrypt AES 方法 这是我的代码 var txtloginKod Some String var key CryptoJS enc Ut
  • GCC 无法使用 init-capture 捕获指向模板类型的“this”指针

    模板类可以捕获自己的thislambda 中的指针 template
  • 如何获取 Git 中 master 分支的默认值?

    由于简短 GitHub 建议使用main代替master对于默认分支 我们怎样才能得到这个名字 因为编写有弹性的 Git 别名 如何获取 Git 中 master 分支的默认值 我已经搜索过 但没有任何解决方案git 如何获取默认分支 对我
  • 如何实现 getline() 的超时? [复制]

    这个问题在这里已经有答案了 我想从命令行读取一个字符串getline 在c 中 为此我想添加一个计时器5秒 如果没有读取到字符串 则程序将终止 我怎样才能做到这一点 好的 等等5秒 和terminate如果没有输入 include
  • 为什么浏览器不会因语法错误抛出异常?

    我不小心写了一个错误的 JavaScript 语法 我是这么认为的 code is var temp temp a 34 height 34 should fail here temp b 56 jsfiddle 语法是否正确 Thanks
  • 如何修复 PHP 中的“标头已发送”错误

    这个问题的答案是社区努力 编辑现有答案以改进这篇文章 目前不接受新的答案或互动 运行我的脚本时 我收到几个如下错误 警告 无法修改标头信息 标头已由 输出从 some file php 12 开始 in 一些 文件 php on line