编程 Caddy 2.11.6 的 60 秒 idle 超时掐断了 SSE,2.11.7 修掉回归并引入 RFC 10036 的 Incremental 头

2026-10-15 21:03:21

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 不再删掉输入最末尾的那个开大括号。
复制全文 生成海报 Caddy SSE 反向代理 HTTP 2

推荐文章

程序员茄子在线接单