ThinkPHP 6 需手动配置中间件启用 Gzip 压缩,推荐在全局中间件如 app/middleware/CompressMiddleware.php 中实现;需确认 PHP 启用 zlib 扩展,并依据 Accept-Encoding 请求头判断是否压缩,避免硬压导致兼容问题。

ThinkPHP 6 开启 Gzip 响应要改哪里
默认不开启,得手动配置中间件或响应对象。核心是让 Response 对象在发送前触发 gzip 压缩,而不是靠 Nginx 或 Apache 代劳——否则框架层的 JSON、HTML 响应体可能绕过压缩。
- 推荐在全局中间件中统一处理,比如
app/middleware/CompressMiddleware.php - 不要在控制器里手动调用
ob_gzhandler,容易和框架输出缓冲冲突 - 确认 PHP 已启用
zlib扩展(php -m | grep zlib),否则gzencode会报错
为什么 Response::send() 不自动压缩
ThinkPHP 的 Response 类默认只做内容类型设置和状态码写入,不干预传输编码。gzip 是 HTTP 层的编码协商行为,需要显式设置 Content-Encoding: gzip 头,并对响应体做压缩——框架没做这步,因为不是所有环境都支持,且可能干扰调试。
- 浏览器发请求时带
Accept-Encoding: gzip,服务端才该响应 gzip;直接硬压会导致非 gzip 客户端解码失败 - ThinkPHP 6.1+ 的
Response支持compress()方法,但仅限字符串内容,不自动识别请求头 - 若用 Swoole 或 RoadRunner,还需检查服务器是否已接管压缩逻辑,重复压缩会出错
最简可靠的中间件写法
不依赖第三方包,用原生 gzencode + 请求头判断,兼容性好、无额外依赖。
<?php
namespace app\middleware;
use think\Response;
class CompressMiddleware
{
public function handle($request, \Closure $next)
{
$response = $next($request);
if ($response instanceof Response &&
$request->header('accept-encoding') &&
strpos($request->header('accept-encoding'), 'gzip') !== false &&
!headers_sent() &&
extension_loaded('zlib')) {
$content = $response->getContent();
if (strlen($content) > 200) { // 小于 200 字节压缩意义不大,还可能变大
$compressed = gzencode($content, 6);
$response->header('Content-Encoding', 'gzip')
->header('Vary', 'Accept-Encoding')
->content($compressed)
->header('Content-Length', strlen($compressed));
}
}
return $response;
}
}
- 注意判断
headers_sent(),避免“headers already sent”错误 -
Content-Length必须重设,否则客户端收不到完整数据 - 压缩级别选 6(默认),太高(9)影响响应延迟,太低(1)压缩率差
Chrome 显示“Failed to load response data”怎么办
这是前端调试时最常见的假问题,本质是 DevTools 无法自动解压 gzip 响应体,不代表传输失败。
立即学习“PHP免费学习笔记(深入)”;
- 看 Network 面板的
Size列:如果显示 “xx B transferred, xx KB resources”,说明 gzip 生效了 - 用
curl -H "Accept-Encoding: gzip" -I http://your-site.com/api/test检查响应头是否有Content-Encoding: gzip - 别在
Response::create()->withHeader()后再调compress(),顺序错会导致 header 被覆盖
真正要注意的是缓存——如果用了 Redis 或文件缓存,得确保缓存的是原始内容,而不是已经 gzip 过的二进制数据,否则下次读出来直接吐乱码。



















