php输出json前必须设置content-type: application/json;需用header('content-type: application/json; charset=utf-8')并确保无bom、无前置输出,配合json_encode($data, json_unescaped_unicode | json_throw_on_error)和exit保证响应纯净。

PHP 输出 JSON 前必须设置 Content-Type: application/json
不设这个头,浏览器或客户端会当成 text/html 或 text/plain 解析,导致解析失败、中文乱码、自动下载等现象。尤其在 Axios、fetch 或小程序里,response.json() 会直接抛错。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
-
header('Content-Type: application/json; charset=utf-8');必须在echo或json_encode()之前调用,且不能有任何输出(包括空格、BOM、echo过的字符串) - 如果用了框架(如 Laravel、ThinkPHP),别手动写
header(),优先走框架的响应构造方法,否则容易和框架的 header 冲突 - UTF-8 BOM 是隐形杀手:用编辑器确认 PHP 文件是「无 BOM 的 UTF-8」,否则
headers already sent错误必现
json_encode() 的常见坑与安全写法
直接 echo json_encode($data) 看似简单,但默认不转义中文、不处理资源类型、不校验编码,上线后常出问题。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 始终加
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES标志,避免中文被转成\u4f60,斜杠被双写 - 加
JSON_THROW_ON_ERROR(PHP 7.3+),让编码失败时抛异常而不是返回false,便于定位数据问题 - 不要对未过滤的用户输入(如
$_GET、$_POST)直接json_encode(),先做类型校验和字段白名单过滤 - 遇到
Resource id #123或Object of class XXX could not be converted to string,说明数据里混了资源句柄或不可序列化对象,需提前unset()或转换
PHP 接口返回标准 JSON 的最小可靠模板
不是所有接口都要套 RESTful 规范,但至少得让前端能稳定解析、不报错、不丢数据。
实操建议(纯原生 PHP 场景):
- 统一用
exit(json_encode([...], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR));结束脚本,避免后续意外输出 - 错误响应也走同一套流程:比如
http_response_code(400); header('Content-Type: application/json; charset=utf-8'); echo json_encode(['code' => 400, 'msg' => '参数缺失'], JSON_UNESCAPED_UNICODE); exit; - 禁止在 JSON 前后拼接 HTML、注释、空行、
var_dump()—— 即使是开发环境,也要保持输出干净 - 如果启用了
output_buffering,记得ob_end_clean()清掉已有缓冲,否则头部或空白字符会混进 JSON
调试时怎么看是不是真 JSON?
很多“看着像 JSON”的响应其实不是标准 JSON,比如多了换行、HTML 注释、PHP 错误警告,或者 Content-Type 错了。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 用
curl -I http://your-api.com/xxx检查响应头里有没有Content-Type: application/json - 用
curl -s http://your-api.com/xxx | python3 -m json.tool验证是否可被 Python JSON 解析(比浏览器更严格) - Chrome DevTools 的 Network → Response 标签页里,右键“Open in new tab”:如果是真 JSON,会高亮显示;如果出现乱码、下载弹窗、或显示 HTML,说明有问题
- 注意 Nginx/Apache 的
add_header或Header set可能覆盖 PHP 的header(),优先在 PHP 里设,或确认 Web 服务器配置没冲突
真正难的不是写那几行代码,而是确保整个请求生命周期里没有任何地方偷偷输出、改头、缓存旧响应——尤其是 include 的公共文件、error_reporting 开启时的 warning、以及日志写入失败触发的异常输出。











