必须关闭nginx的proxy_buffering、fastcgi_buffering和gzip,并禁用php output_buffering;需在location块中配置proxy_buffering off; fastcgi_buffering off; gzip off; add_header x-accel-buffering no; proxy_read_timeout 300; php端设置sse头、关闭缓冲、每条消息后执行ob_flush()和flush()。

PHPEnv 是一套集成环境(含 Nginx + PHP-FPM),默认配置对 SSE 极不友好——proxy_buffering 和 fastcgi_buffering 全部开启,加上 PHP 的 output_buffering 和 Nginx 的 gzip,四层缓冲叠在一起,前端基本收不到实时数据。
关闭 Nginx 代理层缓冲(关键一步)
PHPEnv 中的 Nginx 配置通常位于 /phpenv/nginx/conf/vhost/xxx.conf 或类似路径。找到你对应站点的 location 块(比如匹配 /api/sse 或 /events),必须添加以下三行:
-
proxy_buffering off;—— 关闭 Nginx 对后端响应的缓冲,否则它会攒够 4k 或等超时才吐数据 -
proxy_cache off;—— SSE 是实时流,缓存完全违背语义,且可能截断消息边界 -
add_header X-Accel-Buffering no;—— 显式告诉 Nginx 不要干预响应流,比仅关proxy_buffering更可靠
别漏掉超时设置:proxy_read_timeout 300;(至少匹配你的最长导出耗时),否则连接会在 60 秒后被 Nginx 主动断开。
禁用 PHP-FPM 层的 fastcgi_buffering
PHPEnv 默认启用 fastcgi_buffering,它会把 PHP-FPM 的输出再缓一次。在同一个 location 块里补上:
-
fastcgi_buffering off;—— 注意不是fastcgi_buffer_size,后者是缓冲区大小,前者才是开关 - 同时确认
fastcgi_pass指向的是正确的 PHP-FPM socket 或地址(如127.0.0.1:9000)
如果这行没加,即使 PHP 代码里 flush() 了,Nginx 仍会把整段输出当做一个块转发。
PHP 脚本内必须做的三件事
光靠 Nginx 配置不够,PHP 层必须同步“松手”:
- 开头立刻设响应头:
header('Content-Type: text/event-stream');、header('Cache-Control: no-cache');、header('X-Accel-Buffering: no'); - 关闭 PHP 输出缓冲:
ini_set('output_buffering', 'off');;若已调用ob_start(),需配ob_end_flush()或全程避免使用 - 每条消息后严格执行:
echo "data: ...\n\n"; ob_flush(); flush();——ob_flush()清用户缓冲,flush()推给 SAPI,顺序不能反
常见错误:只写 echo 不 flush,或用 json_encode() 后忘了补 "\n\n",导致浏览器解析失败。
额外干扰项:gzip 和 SSL 的隐性影响
PHPEnv 默认常开启 Nginx gzip,它会压缩整个响应体,破坏 SSE 的 \n\n 分隔结构:
- 在对应
location块中加gzip off;(不要依赖全局配置,局部覆盖更安全) - HTTPS 下延迟更明显?不是证书问题,而是 SSL 握手后 Nginx 更倾向启用缓冲。确保
proxy_buffering off在server { listen 443 ssl; ... }的 location 内也生效 - Apache 用户注意:PHPEnv 不含 Apache,但若混用,需禁用
mod_deflate并加SetEnv no-gzip 1
真正卡住的往往不是代码逻辑,而是某一层缓冲没关干净——检查 Nginx error log 是否有 upstream sent too big header 类报错,那说明缓冲区溢出,得调大 proxy_buffer_size 而非关错开关。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











