Caddy 在 10 月 1 日和 10 月 3 日连续发布 v2.11.6 与 v2.11.7,中间只隔了一天多。2.11.6 是一次"安全加固 + 特性"的大补丁,但引入的默认空闲超时直接打断了流式响应,2.11.7 专门来修这些回归。本文记录两版的关键变化、破坏性变更与实测结论。


一、时间线

版本发布时间(UTC)性质
v2.11.42026-06-03上一个稳定版
v2.11.62026-10-01 15:21安全加固 + 新特性,引入回归
v2.11.72026-10-03 05:58专修 2.11.6 回归

官方在 2.11.7 的说明里直接写:

This patch release fixes regressions from 2.11.6… If you’re on 2.11.6, we recommend upgrading.

也就是说:2.11.6 不值得停留,要么停在 2.11.4,要么直接上 2.11.7。


二、v2.11.6:安全修复是重点

安全修复(11 项)

最需要注意的三条:

修复说明
forward_auth + reverse_proxy 同路由时,请求可能发到错误的上游连接GHSA-6365-7ppr-5r92,反代认证场景,最重要
handle_path / uri strip_prefix 现在规范化路径防止绕过基于路径的鉴权
path_regexp 归一化 Windows 反斜杠补完 CVE-2026-52844

其余包括:101 Switching Protocols 响应剥离 hop-by-hop 头、Windows 8.3 短名每段路径都拒绝、FastCGI 不再把 Proxy 头当 HTTP_PROXY 传给后端(HTTPoxy)、粘性会话 cookie 哈希改为常量时间比较、Admin API 远程访问路径归一化、超大请求体返回 413、ETag 碰撞修复。

破坏性变更

  1. Go 1.26 成为编译最低版本(影响自编译与插件)
  2. 请求头默认上限 16 KiB(原 Go 默认 1MB),超限返回 431
  3. 新增默认 1 分钟空闲读写超时 —— 请求体读 / 响应写完全无进展超过 60 秒就断开;写与写之间的暂停(SSE 那种)不算
  4. 含 . 的请求头被丢弃(沿用 2.11.4 对下划线的处理),可用 expected_dot_headers 白名单
  5. 通配符站的 client_auth 不再继承给有独立 site block 的具体域名
  6. 配置校验变严:重复 named_routes、重复 forward_auth uri、非法权重、歧义 map 输入等从"静默接受"变为报错
  7. Admin API /load 无效配置改返回 400(原来返回 200 + 两个拼接的 JSON)
  8. method 匹配值统一转大写

其他亮点

url_pattern 请求匹配器(URLPattern 标准)、Slowloris 防护与新增 timeouts 指令、tls_automate_names 全局选项、expected_underscore_headers、HTTP/3 在低 MTU 链路可用、encode 下的 SSE 即时流式、优雅停机会等上一版配置遗留的服务器、一批反向代理修复与性能优化。


三、v2.11.7:把 2.11.6 的坑填了

1. 空闲超时引发的崩溃与断流

这是 2.11.6 最大的问题,也是"默认 1 分钟超时"这条破坏性变更的真实后果:

  • HTTP/2 反代 panic(空指针):handler 已返回但反代仍在读请求体(#8101)
  • HTTP/1.1 SSE 被精准掐断:用 POST 打开的流式响应,在读完 body 后正好 60 秒被切(#8103)
  • HTTP/2 安静的 SSE:两次写之间停顿超过 write_idle(默认 1 分钟)→ 被 reset 成 stream error(#8118 / #8119)

三条都在 2.11.7 合入修复(#8107、#8119)。

教训:新版本的"安全/健壮性"默认值,往往就是下一次回归的来源。上线前盯紧 release note 里的 Breaking changes,尤其是超时类。

2. 占位符回归

2.11.6 起,缺失的 cookie / TLS 占位符会输出字面量 {http.request.cookie.*}(应为空字符串),2.11.7 已修(#8019)。

3. 支持 Incremental 头(RFC 10036)

这是 NGINX 私有 X-Accel-Buffering 的标准替代品:

  • 上游返回 Incremental: ?1 → reverse_proxy 立即转发(等同 flush_interval -1),encode 也直接流式
  • 若 request_buffers / response_buffers 会阻止增量转发 → 返回 501 而不是静默缓冲(RFC 要求),并可用 proxy_status_name 添加 Proxy-Status 头说明原因
  • 对 Mercure、SSE、其他流式应用友好

4. 其他改进

  • TLS 握手更快:无人订阅证书事件且未开 debug 日志时,不再为每次握手构建事件数据,证书查找约快一倍
  • Unix socket:reload 移走监听器后立即关闭并删除旧 socket(原来客户端会挂到约 2 分钟后的 GC)
  • JSON set 里多个 Set-Cookie 分开发出(原来逗号合并成一条,客户端解析不了)
  • caddy fmt 不再吃掉输入末尾的 {

四、Docker 镜像实测

由于是容器化部署,镜像是否上架直接决定能不能升级。实测 Docker Hub:

caddy:2.11.6   已推送  (2026-10-02 20:54 UTC)
caddy:2.11.7   404     (尚未推送)

结论:跳过 2.11.6,等 2.11.7 上架。 2.11.6 既有 HTTP/2 反代崩溃,也有 SSE 60 秒断流,官方也建议升级。


五、升级前的检查清单

1. 用新镜像做一次性配置体检

caddy validate 只解析配置、不监听端口、不申请证书、不碰线上流量:

docker run --rm -v /你的路径/Caddyfile:/etc/caddy/Caddyfile:ro \
  caddy:2.11.7 validate --config /etc/caddy/Caddyfile

官方镜像的 ENTRYPOINT 是 caddy,直接接子命令即可。

2. 它查不了的东西(运行时行为,只能上线后观察)

变更validate 能否发现
配置语法、重复项、歧义输入能
请求头 16 KiB 上限 → 431不能
空闲超时断长连接不能
含 . 的头被丢弃不能
通配符站 client_auth 不再继承不能

3. 升级后重点观察:反代的长连接、SSE、大文件慢速上传是否被断。


六、小结

  • 2.11.6:11 项安全修复值得要,但默认 1 分钟空闲超时与 16 KiB 头上限属于行为变更
  • 2.11.7:修掉 2.11.6 的崩溃与断流回归,另加 RFC 10036 Incremental 头支持
  • 策略:不在 2.11.6 停留,等 2.11.7 镜像上架后,先 validate 体检,再改 compose 的 image tag