PHP实时输出需关闭output_buffering和zlib.output_compression,Node.js用res.write()配合flushHeaders(),反向代理下PHP需额外配置Nginx,而Node默认更友好。
PHP 默认开启输出缓冲(output_buffering),不关闭就根本不会“实时”——哪怕你调用 flush() 和 ob_flush(),数据也卡在 PHP 层出不去。确认方式:运行 phpinfo() 查找 output_buffering 值,非 Off 或 0 都不行。
生效方式有三类,按优先级排序:
output_buffering = Off(最彻底,推荐)ini_set('output_buffering', 'Off');(仅对当前脚本生效,但某些 SAPI 如 CLI 下无效)ob_end_flush() 再关缓冲,但不如直接关干净注意:zlib.output_compression 也得关,否则压缩层会拦截流式输出。
Node.js(原生 HTTP 模块或 Express)默认不缓冲响应体,res.write() 就是实时的,但有两个关键前提:
res.writeHead() 或首次 res.end() / res.write() 触发自动 header 发送)res.end(),否则连接关闭,后续 write() 无效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 用的,防止它二次缓存。
Apache、Nginx 这类代理默认启用响应缓冲,尤其对非 chunked 编码或小响应体更激进。PHP 和 Node 在此场景下行为不一致:
output_buffering,Nginx 仍可能等满 4KB 或超时才转发(需配 proxy_buffering off 和 chunked_transfer_encoding on)proxy_buffering 影响)header('Transfer-Encoding: chunked'))验证是否真实时?用 curl -N http://localhost/endpoint(-N 禁用 curl 自身缓冲),观察输出节奏。
这是最常被问的问题,根源不在 PHP 本身,而在整条链路的多层缓冲叠加:
ob_flush() 只清 PHP 用户缓冲,flush() 才推给 Web 服务器
gzip off 或设 gzip_min_length 0
str_repeat(' ', 1024))可绕过flush(),必须用 php-cgi 或命令行模式验证真正端到端可控的实时输出,Node 更轻量、链路更短;PHP 则需逐层排查,从 php.ini → Web server config → CDN → 浏览器,缺一不可。