Caddy 在 10 月 1 日和 10 月 3 日连续发布 v2.11.6 与 v2.11.7,中间只隔了一天多。2.11.6 是一次"安全加固 + 特性"的大补丁,但引入的默认空闲超时直接打断了流式响应,2.11.7 专门来修这些回归。本文记录两版的关键变化、破坏性变更与实测结论。
一、时间线
| 版本 | 发布时间(UTC) | 性质 |
|---|---|---|
| v2.11.4 | 2026-06-03 | 上一个稳定版 |
| v2.11.6 | 2026-10-01 15:21 | 安全加固 + 新特性,引入回归 |
| v2.11.7 | 2026-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 碰撞修复。
破坏性变更
- Go 1.26 成为编译最低版本(影响自编译与插件)
- 请求头默认上限 16 KiB(原 Go 默认 1MB),超限返回
431 - 新增默认 1 分钟空闲读写超时 —— 请求体读 / 响应写完全无进展超过 60 秒就断开;写与写之间的暂停(SSE 那种)不算
- 含
.的请求头被丢弃(沿用 2.11.4 对下划线的处理),可用expected_dot_headers白名单 - 通配符站的
client_auth不再继承给有独立 site block 的具体域名 - 配置校验变严:重复
named_routes、重复forward_auth uri、非法权重、歧义map输入等从"静默接受"变为报错 - Admin API
/load无效配置改返回400(原来返回 200 + 两个拼接的 JSON) 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