会。启用 zlib.output_compression 或 ob_gzhandler 会导致 flush() 阻塞,因压缩在缓冲区末尾一次性完成;需检查并禁用两者,并注意 Nginx/Apache 反向代理层可能二次 gzip。

PHP 实时输出时 gzip 会阻塞 flush() 吗?
会。启用 zlib.output_compression 或通过 ob_gzhandler 开启的 gzip 压缩,本质上是在输出缓冲区末尾才完成压缩并一次性发送数据,这与实时输出(如用 echo + flush() + ob_flush() 推送进度条、日志流)的目标直接冲突。
如何确认当前是否启用了输出 gzip?
检查两个关键配置项:
-
zlib.output_compression:在php.ini或运行时用ini_get('zlib.output_compression')查看,值为1或非空字符串即开启 -
output_handler:若其值为ob_gzhandler(可通过ini_get('output_handler')获取),也意味着 gzip 已介入输出链
只要其中任一开启,flush() 就无法让浏览器“实时看到”未压缩前的原始 chunk —— 数据被卡在 zlib 缓冲区里,直到脚本结束或缓冲区满才发出。
实时输出场景下怎么安全关掉 gzip?
不能只靠 ini_set('zlib.output_compression', '0'),因为该设置在输出已启动后无效;更可靠的方式是运行时主动清除压缩 handler:
立即学习“PHP免费学习笔记(深入)”;
// 在输出开始前执行
if (ini_get('zlib.output_compression')) {
ini_set('zlib.output_compression', '0');
}
if (function_exists('ob_end_clean') && ob_get_level()) {
ob_end_clean();
}
// 确保没有其他 output_handler 干扰
if (ini_get('output_handler') === 'ob_gzhandler') {
ini_set('output_handler', '');
}
// 手动开启干净的输出缓冲(可选)
ob_implicit_flush(0);
ob_start();
注意:ob_gzhandler 一旦被注册进 output_handler,仅改 zlib.output_compression 不足以解除它;必须显式清空 output_handler 配置或确保它从未被设为 ob_gzhandler。
Apache/Nginx 反向代理层还会偷偷 gzip 吗?
会,而且更隐蔽。即使 PHP 层关了 gzip,Nginx 的 gzip on 或 Apache 的 mod_deflate 仍可能对响应体二次压缩,导致浏览器收到 gzip 流但无法逐块解压渲染。
解决方法:
- Nginx:在对应 location 中加
gzip off;,或更精准地用gzip_disable "msie6";配合gzip_vary off;减少干扰 - Apache:在
.htaccess或虚拟主机配置中,用SetEnv no-gzip 1或BrowserMatch ^Mozilla/4 gzip-only-text/html避免误压 - 通用兜底:PHP 中输出前加
header('Content-Encoding: identity');,明确告知中间件“别再压了”
真实线上环境里,Nginx 层的 gzip 往往比 PHP 层更容易被忽略,调试时用 curl -I 检查响应头里的 Content-Encoding 是最直接的验证方式。











