PHP输出缓冲必须关闭,否则SSE无法实时推送;需设置正确响应头、严格遵循消息格式、处理连接断开与重连,并防止Web服务器及PHP-FPM中间层缓存。

PHP 输出缓冲必须关掉,否则 SSE 不会实时
SSE(Server-Sent Events)依赖服务端持续写入响应流,而 PHP 默认开启输出缓冲(output_buffering),会导致数据攒在缓冲区里不发出去。不关它,前端永远收不到第一条消息。
实操建议:
- 脚本开头加
ob_end_flush()和ob_flush(),再调用flush();更稳妥的做法是直接禁用:ini_set('output_buffering', 'Off')、ini_set('zlib.output_compression', 'Off') - Apache 下还要确认
mod_deflate没对 SSE 响应做压缩(它会拦截并缓存响应),Nginx 则需关掉gzip或用gzip off;针对 SSE 路由 - 如果用了 PHP-FPM,
buffer_size设为 0(或0s)避免 FPM 层缓存
header 设置不能错,Content-Type 和 Cache-Control 是关键
SSE 要求响应头严格符合规范,漏掉或写错一个,浏览器就当普通 HTTP 响应处理,不会建立 EventSource 连接。
必须设置的 header:
立即学习“PHP免费学习笔记(深入)”;
-
header('Content-Type: text/event-stream')—— 类型错成application/json或漏掉,连接直接失败 -
header('Cache-Control: no-cache')—— 浏览器可能缓存初始响应,后续推送全失效 -
header('Connection: keep-alive')—— 显式声明长连接,避免代理/CDN 中断 - 可选但推荐:
header('X-Accel-Buffering: no')(Nginx)防止其内部缓冲
每条消息必须以 data: 开头,结尾用双换行
SSE 协议规定消息格式极其简单,但容错极低:字段名必须小写,冒号后要空格,每条消息末尾必须是两个 \n(即 \n\n)。少一个换行,浏览器就卡住不解析。
正确示例:
data: {"status":"ok","time":1716234567}
data: hello
常见错误:
- 用
echo "event: ping\ndata: alive\n\n"却忘了echo默认不带换行,得手动补\n - JSON 字符串里混入 HTML 实体或未转义双引号,导致前端
JSON.parse()报错 - 循环中没控制频率,高频
echo+flush()可能触发 Apache 的TimeOut或 Nginx 的proxy_read_timeout
客户端 EventSource 连接断开后,重连逻辑得自己管
PHP 无法主动感知客户端断开(除非用心跳+超时检测),而浏览器 EventSource 在网络抖动或服务重启后会自动重连——但它只按固定规则重试(首次延迟 ~5s,之后指数退避),且不会传 cookie 或自定义 header。
实际要注意的点:
SSE 看似简单,真正跑稳的关键不在“怎么发”,而在“怎么不让它被中间层吞掉”和“怎么扛住连接反复断”。很多问题不是代码写错了,而是 Apache/Nginx/PHP-FPM 的默认缓冲策略在背后悄悄改写了你的响应流。











