PHP 输出 header 注意什么

wen PHP项目 1

PHP输出header的7个致命陷阱:90%的开发者都踩过这些坑

目录导读

  1. header()的基本规则 – 输出前不能有任何字符
  2. 常见错误:BOM头与空格 – 悄无声息的页面错乱
  3. 重定向(Location)的绝对路径误区
  4. 缓存控制(Cache-Control)与状态码的灵活搭配
  5. 文件下载场景的header组合技
  6. 多次header调用的覆盖与冲突
  7. 跨域(CORS)与安全头的现代实践

header()的基本规则:输出前不能有任何字符

header()函数用于发送原始HTTP头信息。最重要的铁律是:在调用header()之前,不能有任何输出(包括HTML、空格、BOM头),否则会报“Cannot modify header information - headers already sent”错误。

PHP 输出 header 注意什么

代码示例(错误示范)

<?php
echo "hello"; // 已经输出了
header("Location: https://example.com");

正确做法:确保PHP文件开头没有<?php之前的空格/换行/UTF-8 BOM,并且不要输出任何内容。


常见致命错误:BOM头与隐形空格

很多开发者忽略UTF-8 BOM头(就是文件开头的三个字节EF BB BF),当使用带BOM的编辑器保存PHP文件时,BOM会被直接输出到浏览器,导致header()失效。

解决方案

  • 使用无BOM的UTF-8编码(如VS Code选“UTF-8 without BOM”)。
  • 检查包含其他文件时,被包含文件也可能带BOM。
重定向(Location)的绝对路径误区

Header("Location: ...") 在PHP中必须使用绝对URL,否则部分浏览器会忽略重定向。

// ❌ 错误
header("Location: /page.php");
// ✅ 正确
header("Location: https://example.com/page.php");

补充:如需过段时间跳转,可配合header("Refresh: 5; url=https://example.com"),但建议用JS或meta替代。


缓存控制(Cache-Control)与状态码的搭配

输出header时,状态码要与内容类型匹配,404页面需要http_response_code(404),否则浏览器可能缓存为200。

缓存控制组合

header("Cache-Control: no-cache, no-store, must-revalidate"); // 防止缓存
header("Pragma: no-cache"); // HTTP/1.0兼容
header("Expires: 0");

若输出JSON,必须同时设置:

header("Content-Type: application/json; charset=utf-8");

文件下载场景的header组合技

当处理文件下载时,需要至少三个header

header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"".basename($file)."\"");
header("Content-Length: ".filesize($file));
readfile($file);

这里注意:filename需要URL编码或加引号,否则中文名会乱码,推荐用rawurlencode()


多次header调用的覆盖与冲突

同一个HTTP响应中,多次调用header对同一个键,会以最后一次为准(除非使用“设置多个值”的语法,如Set-Cookie)。

header("X-Custom: first");
header("X-Custom: second");
// 最终结果是X-Custom: second

setcookie()函数注意:它会自动发送Set-Cookie头,多次调用没问题,但参数名冲突时,建议使用数组setcookie("key[]", "val")


跨域(CORS)与安全头的现代实践

现在前后端分离场景下,Access-Control-Allow-Origin必须按需设置:

header("Access-Control-Allow-Origin: https://trusted-site.com");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization");

安全头推荐设置(防御XSS/点击劫持/嗅探):

header("X-Frame-Options: SAMEORIGIN");
header("X-Content-Type-Options: nosniff");
header("Referrer-Policy: strict-origin-when-cross-origin");
header("Content-Security-Policy: default-src 'self'");

问答专区(核心FAQ)

问1:为什么我写了header("Location:")却跳转失败? 答:检查三处:①前面是否有任何echo/HTML输出/BOM(用die()exit()在header后确保立即终止);②URL是否为绝对路径;③是否ob_start()开启缓冲区——如果开着,可以先ob_end_clean()清空缓冲区。

问2:header("Content-Type: text/html; charset=utf-8")后,页面中文还是乱码? 答:这可能是因为PHP文件本身保存的编码不是UTF-8(比如是GBK),并且数据库连接也需设置UTF-8,用mb_internal_encoding("UTF-8")mysql_set_charset('utf8')确保全链路统一。

问3:动态生成图片/PDF时,header怎么配合? 答:图片用header("Content-Type: image/png"),PDF用header("Content-Type: application/pdf"),同时加header("Content-Disposition: inline; filename=\"...\"")表示浏览器内显示,attachment则强制下载。

问4:能否在HTML页面中间写header? 答:绝对不可以,如果需要,必须用ob_start()开启输出缓冲,然后在任意位置调用header,最后ob_end_flush()输出,但更推荐逻辑控制——在页面最顶部处理所有header。

问5:如何调试header是否发送成功? 答:用PHP的内置函数headers_list()查看已设置的header,或者在Chrome开发者工具的Network标签下查看响应头,可开启error_reporting(E_ALL),并在php.ini设置display_errors=On以捕获警告。


输出header虽是个小函数,但关系到SEO抓取、页面性能、安全防护。核心三原则:无输出、得用绝对URL、配上状态码,建议开发时将PHP文件的编码、空白字符管理纳入代码规范,如需排查,优先使用headers_sent()函数检测问题位置:

if (!headers_sent()) {
    header("X-Example: test");
}

掌握这7个注意点,你的PHP HTTP交互将更加稳健,也能在搜索引擎的爬虫审计中拿到更高好感分。

抱歉,评论功能暂时关闭!