Caddy 可以自动申请和续期 HTTPS 证书。自动 HTTPS 依赖域名配置、DNS、ACME 验证入口和证书存储;任何一项不可用,签发或续期都会失败。容器已经启动,只能说明 Caddy 进程正在运行。
自动 HTTPS 的生效条件
Caddy 的 Automatic HTTPS 文档(在新标签页打开)要求以下条件同时成立:
- 配置中有完整域名,而不是只有
:80、:443或http://地址; - A、AAAA 或 CNAME 最终指向当前入口;
- Caddy 能监听验证所需端口,或者流量能转发到对应端口;
- Caddy 数据目录可写,并且在服务重启和容器重建后仍然存在。
最小 Caddyfile 包含域名和上游:
example.com {
reverse_proxy app:8080
}
Caddy 识别到公开域名后,会管理证书并添加 HTTP 到 HTTPS 的跳转。配置写成 http://example.com 会关闭该站点的自动 HTTPS。
ACME 验证方式取决于入口条件
证书颁发机构必须确认申请者控制当前域名。Let’s Encrypt 的验证方式说明(在新标签页打开)区分了三种常用挑战:
| 验证方式 | 验证入口 | 适用情况 | 常见失败 |
|---|---|---|---|
| HTTP-01 | http://域名/.well-known/acme-challenge/...,端口 80 | 普通公网网站 | 80 未开放、DNS 指错、请求被其他代理接走 |
| TLS-ALPN-01 | 域名的 443 端口和特定 TLS 握手 | 443 可直接到达 Caddy | 443 被其他入口终止、SNI 路由错误 |
| DNS-01 | _acme-challenge TXT 记录 | 通配符、内网站点、入口端口不可开放 | DNS API 权限、TXT 传播、权威 DNS 选错 |
HTTP-01 和 TLS-ALPN-01 可以由 Caddy 自动选择。通配符证书只能通过 DNS-01 签发;这要求安装对应 DNS provider 模块,并把最小权限的 API 凭据交给 Caddy。
CA 读取的是权威 DNS 返回的地址,而不是本机缓存。切换记录后直接查询权威 DNS:
dig +short example.com NS
dig @权威DNS地址 example.com A
dig @权威DNS地址 example.com AAAA
AAAA 指向旧服务器也会导致验证失败。暂时没有可用 IPv6 入口时,应删除错误的 AAAA,而不是等待客户端自动改走 IPv4。
容器必须持久化 Caddy 数据目录
Caddy 把证书、私钥、ACME 账号和续期状态保存在数据目录。容器中常用 /data,配置目录通常是 /config:
services:
caddy:
image: caddy:2
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
volumes:
caddy_data:
caddy_config:
证书卷不能随发布目录一起清空,也不要在普通部署中运行 docker compose down -v。多个 Caddy 实例共享同一证书存储时,还要使用支持锁和一致性的存储后端;简单复制目录不能替代协调机制。
从公网确认签发结果
先检查配置,再从服务外部验证 DNS、HTTP 跳转和 TLS:
caddy validate --config /etc/caddy/Caddyfile
curl -I http://example.com/
curl -I https://example.com/
openssl s_client -connect example.com:443 \
-servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates -ext subjectAltName
检查结果应为:
- HTTP 返回到同一域名 HTTPS 地址的永久跳转;
- TLS 握手成功,证书 SAN 包含当前域名;
- 证书未过期,颁发者和证书链符合预期;
- HTTPS 请求返回应用的预期状态,而不是入口的默认站点。
openssl s_client -status 可以查看服务端是否返回 OCSP stapling 信息,但证书续期监测仍应以到期时间和证书管理日志为主。不同 CA、证书类型和服务端配置下,OCSP 行为并不完全相同。
续期不应依赖人工记日期
Caddy 会在后台管理证书并按需续期。证书有效期仍需独立监测,因为下一次 ACME 验证、存储写入或证书替换都可能失败。
domain=example.com
expiry="$(
openssl s_client -connect "${domain}:443" -servername "${domain}" </dev/null 2>/dev/null \
| openssl x509 -noout -enddate \
| cut -d= -f2-
)"
openssl s_client -connect "${domain}:443" -servername "${domain}" </dev/null 2>/dev/null \
| openssl x509 -checkend $((30 * 24 * 60 * 60)) -noout
printf 'certificate_expires=%s\n' "${expiry}"
证书监控应覆盖剩余有效期、Caddy 证书管理错误日志、80/443 外部可达性,以及数据卷空间和写权限。告警阈值要早于证书到期,给 DNS、CA 或权限故障留下处理时间。
失败时按 ACME 验证顺序排查
| 日志或现象 | 优先检查 | 恢复方式 |
|---|---|---|
NXDOMAIN、找不到授权记录 | 域名拼写、NS、A/AAAA/CNAME | 在权威 DNS 修正记录,等待权威应答稳定 |
| HTTP challenge 超时 | 80 端口、安全组、防火墙、端口转发 | 让公网 80 到达当前 Caddy,或改用 DNS-01 |
| TLS-ALPN challenge 失败 | 443 的终止入口、SNI 和转发关系 | 让验证握手到达负责证书的实例 |
| DNS challenge 找不到 TXT | DNS API 权限、zone 选择、传播时间 | 查询权威 DNS,修正凭据作用域和记录位置 |
| 重建后重复申请 | /data 未持久化或权限改变 | 恢复原卷并修正属主,不要继续创建新卷 |
| CA rate limit | 高频重试或重复域名集合申请 | 停止无效重试,修复根因;测试改用 staging |
Let’s Encrypt 的限额文档(在新标签页打开)会随政策更新。不要把某个限额数字写死进部署脚本;自动化应识别 CA 返回的具体错误和可重试时间。
首次配置或调整 ACME 验证方式时,先使用 staging;确认 DNS、端口和存储都正确后,再切回生产 CA。修改多项配置时合并为一次 reload,避免干扰正在进行的 ACME 事务。
证书链稳定后,再配置对象存储和 CDN 的品牌域名;跨云接入过程见阿里云 OSS 绑定自定义 CDN 域名。
常见问题
Caddy 会自动申请和续期 HTTPS 证书吗?
会。域名、DNS、验证入口和持久化数据目录满足条件时,Caddy 会申请证书、启用 HTTPS、添加 HTTP 跳转并在后台续期。
申请证书一定要开放 80 端口吗?
不一定。HTTP-01 使用 80,TLS-ALPN-01 使用 443,DNS-01 通过 TXT 记录验证。通配符证书必须使用 DNS-01。
Caddy 容器重建后为什么重新申请证书?
通常是 /data 没有持久化,或者新容器无法读取原卷。恢复原数据卷和正确权限后再启动,避免消耗新的签发请求。
怎样确认自动续期真的可用?
检查证书剩余时间、证书管理日志、数据目录持久化和公网 TLS 握手。浏览器当前能访问,只证明现有证书仍可用。
证书申请失败后可以反复重启 Caddy 吗?
不建议。先修正 DNS、端口、权限或验证方式。测试环境使用 ACME staging,避免无效重试触发生产 CA 限额。