Caddy 2.11.6 的 60 秒 idle 超时掐断了 SSE,2.11.7 修掉回归并引入 RFC 10036 的 Incremental 头
信源:Caddy v2.11.7 release notes(2026-10-03),仓库 github.com/caddyserver/caddy。
现象:POST 打开的 SSE,读完 body 后正好 60 秒断
2.11.6(9-30)为缓解 Slowloris,给服务端加了默认 1 分钟的 idle read/write 超时,配置项是 read_body_idle 和 write_idle,可以用 server 级的 timeouts 选项或 per-route 的 timeouts 指令调整。这个方向本身没问题,但引入了两个回归。
第一个是请求体的 idle deadline 活得比设置它的 handler 还长。
HTTP/2 下,反向代理在 handler 返回之后仍读请求体时,Caddy 会 nil pointer dereference panic(#8101)。
HTTP/1.1 下,带 body 的流式响应会在读完 body 后正好 60 秒被掐断(#8103)。典型场景是用 POST 打开 SSE 的客户端:body 早已读完,连接本该继续保持,但那个 idle deadline 还挂在请求体上,60 秒到点就断。
两者都在 #8107 修复。
第二个回归在写侧。HTTP/2 下,两次写入之间停顿超过 write_idle(默认 1 分钟)的流式响应会被 stream error 重置(#8118 / #8119)。安静的 SSE 流最容易命中:没有数据可发时本来就不该写,旧逻辑把「没写」等同于「卡住」。按修复后的语义,只有真正卡住的写才该算超时。
排查时先确认版本落在 2.11.6:读侧的症状是到点断流或直接 panic,写侧是 stream error 重置。临时绕过可以调大 read_body_idle / write_idle,但那只是把窗口拉长,升级到 2.11.7 才是修复。
Incremental 头:X-Accel-Buffering 的标准替代
2.11.7 新增 Incremental 头字段(RFC 10036),定位是 NGINX 私有头 X-Accel-Buffering 的标准替代。
上游响应带 Incremental: ?1 时,reverse_proxy 会立即转发,语义等同 flush_interval -1;encode 也走流式而非缓存。Mercure、SSE 这类场景直接受益。
如果 request_buffers / response_buffers 的配置会阻止增量转发,Caddy 按 RFC 返回 501 Not Implemented,而不是静默缓冲——静默缓冲是流式场景里最难查的失败模式。新增的 proxy_status_name 选项可以给这些响应加上 Proxy-Status 头,方便在客户端侧区分。
顺带修掉的部分
- 缺失 cookie 时的占位符又变回空值。2.11.6 起
respond头会把{http.request.cookie.}字面输出。 - TLS 握手更快:没有订阅者且 debug 关闭时,CertMagic 不再每次握手都构造事件数据,证书查找大约快一倍,分配次数从 15 降到 10。
- reload 把一个 listener 从 unix socket 移走时,旧 socket 立即关闭并删掉文件。此前客户端连旧路径会挂大约 2 分钟。
headers处理器遇到多个Set-Cookie值时现在分开发送,不再用逗号合并。caddy fmt不再删掉输入最末尾的那个开大括号。