这个坑值得记一笔。Node 20 上用 Express 4.19 写了个流式接口,res.write 写完都调了 flush,本地 3000 端口用 curl 能看到逐条输出,套上 compression 中间件后 curl 只收到一个整包,前端 EventSource 一直停在连接中。

刚开始盯着「响应头在第一次 write 之后才设置,中间件已经决定压缩」,折腾了半个多小时,排掉了。

排查方向其实有限,先把可能性排一排。compression 中间件对 text/event-stream 也会做 gzip 缓冲;调了 res.flush 但没引入 compressing 提供的 flush 方法;用了 res.end 一次性返回,连接根本没保持住。

先看响应头有没有 Content-Encoding

curl -sI http://localhost:3000/stream | grep -i encoding

然后把事件流路径排除在压缩之外

app.use(compression({ filter: (req, res) => !String(res.getHeader('Content-Type')).includes('event-stream') }));

接着设头再写第一个事件

res.setHeader('Content-Type', 'text/event-stream'); res.setHeader('Cache-Control', 'no-cache'); res.write('data: hi\n\n');

末了用 curl 逐行验证输出节奏

curl -N http://localhost:3000/stream

隔了一天再看,现象没变,说明不是偶发。

那几天正好在做别的变更,先把时间线对齐才敢动手。

改完先别撤,确认这几项。响应头里不再有 Content-Encoding,curl 每秒收到一行 data,前端的 onmessage 连续触发。

补一句。压缩中间件按 Content-Type 判断,但流式响应头常常设置得偏晚,显式 filter 比调整中间件顺序更稳。这类问题最好一次改到位,反复小改反而容易把原因搅混。

作者 9823189