接口在浏览器里报 Cannot modify header information - headers already sent (output started at /var/www/api.php:1)。文件第 1 行就是 <?php,前面看不到任何字符,响应体开头却多了 ef bb bf 三个字节。乍看像缓存或者权限的锅,追下去往往不是。

for f in $(find . -name "*.php"); do head -c 3 "$f" | xxd | grep -q "efbb bf" && echo "$f"; done
sed -i "1s/^\xef\xbb\xbf//" /var/www/api.php

追下去是这么回事:编辑器把文件存成了 UTF-8 with BOM,这三个字节在 PHP 标签之前就当作正文输出去了。header() 必须早于任何输出,一旦有字节落地,后面再设状态码或跳转都会报已发送。被 include 的语言包和配置文件同样会带 BOM。

「BOM 响应头」这个问题文档写得含糊,实际行为以当前版本为准,别照旧版本的结论套。一次只改一处,改完立刻验证,几个改动叠在一起,出问题就说不清是谁导致的。

先做最小复现,能稳定跑出来的问题,排查成本会低一个数量级。

具体做法是,用 xxd 或 od 看文件前三个字节 确认是不是 ef bb bf;把编辑器默认编码改成 UTF-8 无 BOM 并重新保存该文件;逐个检查 include 进来的配置文件和语言包 它们也可能带 BOM;入口处加 ob_start() 只能兜底 根因还是清干净 BOM。

验收就看一点,再次请求接口 响应头里不再有 output started at 提示 跳转和状态码都正常返回。

作者 9823189