thinkphp 5.1+ 中 json() 返回中文乱码的根本原因是响应头 content-type 缺少 charset=utf-8,导致浏览器按 iso-8859-1 解析 utf-8 编码的 json;需在 php 层主动设置 application/json; charset=utf-8,且 web 服务器不得覆盖该头。

ThinkPHP 5.1+ 中 json() 返回中文乱码的根源
根本原因不是 JSON 内容本身出错,而是响应头 Content-Type 缺少 charset=utf-8,导致浏览器(尤其旧版 IE、部分移动端 WebView)按 ISO-8859-1 解析 UTF-8 编码的 JSON 字符串。
ThinkPHP 默认的 json() 方法在 5.1+ 版本中已自动设置 application/json,但不强制带 charset —— 这在 Nginx + PHP-FPM 环境下尤为明显,因为 FastCGI 协议本身不传递 charset,全靠 PHP 主动写入响应头。
手动补全 charset=utf-8 的三种可靠方式
优先推荐「全局中间件」方式,避免每个控制器重复写;若仅个别接口需兼容老环境,可用后两种。
- 全局中间件(推荐):在
app/middleware.php中追加闭包,或新建中间件类,在handle()末尾加:if (strpos($response->getHeaderLine('Content-Type'), 'application/json') === 0) { $response = $response->withHeader('Content-Type', 'application/json; charset=utf-8'); } - 控制器内显式调用:
return json($data)->header('Content-Type', 'application/json; charset=utf-8'); - 覆盖系统 JSON 响应类(高级):继承
think\Response,重写init()或send(),确保所有json()输出都带 charset —— 但需注意后续升级可能覆盖自定义逻辑。
default_charset 配置对 json() 无效
很多人误以为在 config/app.php 中设置 'default_charset' => 'utf-8' 就能解决 JSON 乱码,实际上该配置仅影响 view 渲染和 input()->get() 等输入解析,与响应头中的 Content-Type 无关。
验证方法:直接打印 response()->getHeaders(),你会发现 default_charset 不会出现在 Content-Type 值里。
另外注意:ini_set('default_charset', 'utf-8') 同样不影响 json() 响应头,它只作用于 htmlspecialchars() 等函数的默认编码参数。
Apache / Nginx 下还需检查服务器层字符集设置
即使 PHP 层补全了 charset=utf-8,如果 Web 服务器强制覆写了 Content-Type,仍会失效。常见陷阱:
- Apache 的
AddDefaultCharset指令(如设为Off或ISO-8859-1)会覆盖 PHP 发送的 header;应在虚拟主机或 .htaccess 中明确禁用:AddDefaultCharset Off
- Nginx 的
charset指令(如charset utf-8;)会对所有响应统一加 charset,但会覆盖 PHP 的Content-Type值(变成application/json; charset=utf-8✅),而charset_types若未包含application/json,则不会生效 —— 必须显式添加:charset_types application/json text/html text/plain;
最稳妥的做法:PHP 层主动写全 header,Web 服务器不干预 Content-Type,仅负责转发。
乱码问题往往卡在“以为设了就生效”,实际要逐层确认 header 最终输出值 —— 用 Chrome DevTools 的 Network → Response Headers 看真实发送的 Content-Type,比任何配置猜测都准。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











