前端证书是不是又快到期了?手里域名多、子域名更多的时候,每三个月手动申请一次证书、再手动扔进 Nginx、还要记得 reload,这套流程重复几轮之后真的会烦到怀疑人生。我最近把整套签发链路彻底换成了 acme.sh + Docker + 腾讯云 DNSPod API 的自动化方案:DNS 解析自动验证、泛域名证书一次申请、到期自动续签。这篇文章就是我的完整部署记录,所有步骤都是实测跑通的,照着做基本能实现“只要域名还在你手里,证书就永远不会过期”。
这个方案尤其适合这几类人:家里或公司跑了一堆自建服务,比如 Nextcloud、GitLab、Grafana、API 网关,每个服务都想要 HTTPS;或者你在做小程序、App 的后端,需要给多个子域名统一配证书;再或者纯粹是厌倦了每三个月调一次闹钟去续证书。接下来我从方案选型开始讲,再到具体配置、排错,全程带命令和解释。
1. 方案选型拆解:为什么是 acme.sh + DNS API + Docker
1.1 泛域名证书到底解决了什么问题
泛域名证书,也叫通配符证书,一张证书里包含 *.example.com 这样的通配条目。只要 DNS 解析落在 example.com 这个主域下,不管是 blog.example.com、api.example.com 还是 dev.example.com,这张证书都能直接覆盖。
那为什么不给每个域名单独申请一张证书?第一是管理成本,申请、续签、部署、监控,每多一张证书就多一圈事情;第二是浏览器兼容性,一张证书挂满所有子域名,用户访问任何一个子服务都不会出现证书不匹配的告警;第三是新增子域名的时候,你不需要再申请新证书,只要把 DNS 解析指过去,HTTPS 马上就能用。
需要注意一个细节:通配符证书的 *.example.com 并不包含裸域 example.com 本身。所以我实际操作时,申请命令里会同时带 -d example.com -d "*.example.com" 两个域名,等于一张证书把裸域和所有一级子域全覆盖。Let's Encrypt 对泛域名证书的签发是完全免费的,有效期 90 天,所以自动续签就成了刚需。
1.2 为什么验证方式必须用 DNS-01 而不是 HTTP-01
ACME 协议里常用的域名验证方式有两种:HTTP-01 和 DNS-01。
HTTP-01 的流程是向 http://你的域名/.well-known/acme-challenge/xxx 放一个随机文件,证书机构来访问这个文件,能访问到就证明你控制了这个域名。这种方式配置简单,但不支持签发泛域名证书,因为它验证的只是某一个具体域名,没法覆盖通配符。
DNS-01 则是要求你在域名的 DNS 解析记录里添加一条 _acme-challenge 的 TXT 记录,记录值是证书机构下发的随机字符串。证书机构去查询这条 TXT 记录,能查到就证明你对该域名有控制权。因为 DNS 解析本身是域名级别的权限,所以 DNS-01 才能用来签发 *.example.com 这种泛域名证书。
DNS-01 的问题是手动操作太痛苦:每次申请或续签,你要去 DNS 控制台手动加一条 TXT 记录,等生效,验证完再删掉。三个月的周期说长不长,但手动折腾几次就容易漏。所以真正靠谱的方式是让证书签发工具直接调用 DNS 服务商的 API,自动添加和删除 TXT 记录,全程无人工介入。acme.sh 内置了大量 DNS 服务商的 API 插件,腾讯云 DNSPod 正好是支持最完善的那一批。
1.3 容器化部署的价值在哪里
acme.sh 本身是一个纯 Shell 脚本,直接装在宿主机上也能跑。那为什么我还坚持用 Docker 容器部署?
首先是环境隔离。acme.sh 运行时会写一堆配置、证书和日志文件,装到宿主机上会散落在各个目录,卸载也麻烦。用容器之后,所有数据都集中在一个挂载卷里,想迁移就直接把卷拷走;想升级 acme.sh 版本就重新拉镜像,不用管宿主机的依赖。
其次是定时任务不用单独配。acme.sh 官方镜像 neilpang/acme.sh 提供了 daemon 模式,容器启动后内部会自动按周期检查证书是否需要续签。如果直接装在宿主机上,你还得自己写 crontab,还要考虑 crontab 的环境变量、路径问题。
第三是统一运行时。acme.sh 依赖 curl、openssl、cron 这些工具,不同系统版本可能
