PHP实时输出需关闭output_buffering和zlib.output_compression,Node.js用res.write()配合flushHeaders(),反向代理下PHP需额外配置Nginx,而Node默认更友好。

PHP 实时输出必须关掉 output_buffering
PHP 默认开启输出缓冲(output_buffering),不关闭就根本不会“实时”——哪怕你调用 flush() 和 ob_flush(),数据也卡在 PHP 层出不去。确认方式:运行 phpinfo() 查找 output_buffering 值,非 Off 或 0 都不行。
生效方式有三类,按优先级排序:
-
php.ini 中设
output_buffering = Off(最彻底,推荐) - 脚本开头加
ini_set('output_buffering', 'Off');(仅对当前脚本生效,但某些 SAPI 如 CLI 下无效) - 运行前手动清空:先
ob_end_flush()再关缓冲,但不如直接关干净
注意:zlib.output_compression 也得关,否则压缩层会拦截流式输出。
Node.js 实时输出靠 res.write() + res.flushHeaders()
Node.js(原生 HTTP 模块或 Express)默认不缓冲响应体,res.write() 就是实时的,但有两个关键前提:
立即学习“PHP免费学习笔记(深入)”;
- 响应头必须已发送(
res.writeHead()或首次res.end()/res.write()触发自动 header 发送) - 不能提前调用
res.end(),否则连接关闭,后续write()无效 - 若用 Express,需禁用
res.send()类封装(它自动 end),改用res.write()+res.end()手动控制
示例片段:
res.writeHead(200, { 'Content-Type': 'text/plain', 'X-Accel-Buffering': 'no' });
res.write('chunk 1\n');
res.flushHeaders(); // 显式确保 header 已发(尤其搭配反向代理时)
setTimeout(() => res.write('chunk 2\n'), 1000);
X-Accel-Buffering: no 是给 Nginx 用的,防止它二次缓存。
PHP 和 Node 实时输出在反向代理下表现差异大
Apache、Nginx 这类代理默认启用响应缓冲,尤其对非 chunked 编码或小响应体更激进。PHP 和 Node 在此场景下行为不一致:
- PHP 即使关了
output_buffering,Nginx 仍可能等满 4KB 或超时才转发(需配proxy_buffering off和chunked_transfer_encoding on) - Node 默认用 chunked 编码,只要 header 发出且没 end,Nginx 通常逐 chunk 转发(但仍受
proxy_buffering影响) - Cloudflare 等 CDN 会彻底吞掉非 chunked 流式响应,PHP 更容易踩坑(因常漏设
header('Transfer-Encoding: chunked'))
验证是否真实时?用 curl -N http://localhost/endpoint(-N 禁用 curl 自身缓冲),观察输出节奏。
PHP 的 flush() 为什么总不生效
这是最常被问的问题,根源不在 PHP 本身,而在整条链路的多层缓冲叠加:
-
ob_flush()只清 PHP 用户缓冲,flush()才推给 Web 服务器 —— 二者必须成对出现 - Web 服务器(如 Apache 的 mod_deflate、Nginx 的 gzip)可能再加一层压缩缓冲,需关
gzip off或设gzip_min_length 0 -
浏览器也有最小渲染阈值(如 Chrome 约 1KB),输出太短会等满才刷屏;加足够空白(
str_repeat(' ', 1024))可绕过 - 某些 PHP SAPI(如 FPM)根本不支持
flush(),必须用php-cgi或命令行模式验证
真正端到端可控的实时输出,Node 更轻量、链路更短;PHP 则需逐层排查,从 php.ini → Web server config → CDN → 浏览器,缺一不可。











