这是「把自己部署到互联网上」系列的第 28 篇。
上一篇写了单域名 SSL 证书从申请、下载到部署的完整流程。
从申请证书,完成域名验证,下载证书和私钥,放到全局 Nginx 的证书目录,配置 443,检查 Nginx,重载服务,最后验证 HTTPS。
这条链路适合把 HTTPS 跑通,对整个流程有个完整的理解。
接下来,又会遇到一个问题,
假如我有多个子域名,我该如何处理呢,难道说,也是一个一个的申请?
多个子域名应如何处理?
业务发展需要,会产生多个服务,比如 blog.aicultiv.com、www.aicultiv.com、api.aicultiv.com、admin.aicultiv.com,每个域名都单独申请证书,维护会很碎。
每套证书都要申请、下载、放置、配置、续期,过期时间还可能不同。
时间久了,证书维护就会从基础设施变成负担
所以我准备用更适合长期维护的方案:
通配符证书 + acme.sh
通配符证书可以覆盖一组一级子域名。
比如 *.example.com 可以覆盖 blog.aicultiv.com、www.aicultiv.com、api.aicultiv.com。
但它通常不覆盖 a.b.example.com 这种更深层级。如果主域名 example.com 也要使用 HTTPS,常见做法是证书同时包含:
-d example.com
-d '*.example.com'
这样主域名和一级子域名都能用同一套证书。
对我的博客、官网和作品站来说,共用一套证书,这样只需要维护这一套证书即可,与一个应用一个证书相比,极大的减少了工作量。
通配符证书也需要 DNS 验证
证书机构会要求添加类似 _acme-challenge.example.com 的 TXT 记录,用来确认你控制这个域名的 DNS。
手动添加 TXT 记录可以完成签发,但续期时也要重复操作,这就不适合长期维护。
DNS API 的作用,就是让工具自动添加和清理验证记录。
acme.sh 是一个 ACME 客户端
它是一个 Shell 脚本,支持通配符证书、DNS API、定时续期,也支持把证书安装到指定路径,并在续期后执行 reload 命令。
对我的服务器架构设计来说,重要的是证书能自动续期,并且续期后 Nginx 能加载新证书,这个才是我想要的结果。
这两个进行配合,即可实现通配符证书自动续期。
云服务器控制台授权
创建 RAM 用户
登录云服务控制台,创建一个子用户,然后对这个子用户进行授权操作,强烈建议
只给一个操作权限!
只给一个操作权限!
只给一个操作权限!
防止 API Key 泄露之后,可能产生不可承担的后果。

创建 API key
对上面创建的子账户创建一个 API Key 和 Secret
API Key 和 Secret 要妥善保存,只显示一次,记录到只有你自己知道的地方(这里就不贴图了)
最好进行定期更换,减少泄露的风险
Acme.sh
安装
安装 acme.sh 可以使用官方安装方式:
curl https://get.acme.sh | sh -s email=my@example.com
也可以通过 Git 安装:
git clone https://github.com/acmesh-official/acme.sh.git
cd ./acme.sh
./acme.sh --install -m my@example.com
下载失败的可以参考下这个教程
https://github.com/acmesh-official/acme.sh/wiki/Install-in-China
安装后通常会在用户 home 目录下生成 ~/.acme.sh/,并创建定时任务。
重新打开终端后,执行:
~/.acme.sh/acme.sh --help
证书最终要安装到全局 Nginx 容器目录时,需要提前处理好目录权限。
接下来准备 DNS API。不同 DNS 服务商的环境变量不同 (也就是上面创建的 API Key 和 API Secret),Cloudflare、阿里云、腾讯云、华为云都有各自的插件和字段名。
这里不能写死,要查对应平台的 DNS API 文档。
原则只有一个:创建能管理 DNS 验证记录的 API 凭据,然后在服务器上配置对应环境变量。
示意:
export Ali_Key="你的 API Key"
export Ali_Secret="你的 API Secret"
不同平台变量名会有不同,变量以对应插件为准。
如果 DNS 服务商支持按域名或记录类型授权,应尽量使用最小权限。
为了避免重启服务器后变量失效
追加到 ~/.bashrc:
echo 'export Ali_Key="LTAxxxx"' >> ~/.bashrc
echo 'export Ali_Secret="xxxxxx"' >> ~/.bashrc
source ~/.bashrc
申请
申请通配符证书的命令是:
acme.sh --issue \
-d 你的域名.com \
-d '*.你的域名.com' \
--dns dns_xxx
dns_xxx 要替换成你的 DNS 服务商插件名称。
第一次执行时,它会:
- 自动连接阿里云 DNS
- 自动创建 TXT 验证记录
- 向 Let's Encrypt 申请证书
- 删除 TXT 验证记录
签发失败时,先查错误原因,不要反复重试。
常见问题包括 DNS API 凭据错误、权限不足、插件名称写错、域名不在当前 DNS 服务商管理、TXT 记录未生效、触发 CA 频率限制。
部署证书
证书签发成功后,不要直接让 Nginx 引用 ~/.acme.sh/ 里的文件。
acme.sh 官方也建议使用 --install-cert 把证书安装到生产路径。
原因很简单:~/.acme.sh/ 是内部目录,结构可能变化;
生产证书路径由自己规划。
在我的方案里,证书安装到:
/opt/coduty/nginx/certs/example.com/
创建目录:
mkdir -p /opt/coduty/nginx/certs/example.com
执行安装:
acme.sh --install-cert -d example.com \
--key-file /opt/coduty/nginx/certs/example.com/privkey.pem \
--fullchain-file /opt/coduty/nginx/certs/example.com/fullchain.pem \
--reloadcmd "docker exec global-nginx nginx -s reload"
这里最重要的是 --fullchain-file 和 --reloadcmd。
Nginx 应使用完整证书链;
证书续期更新后,也必须让真正提供服务的 Nginx 重新加载。
否则证书文件虽然更新了,没有重载,站点仍可能使用旧证书。
配置完之后,这样不同站点可以引用同一套通配符证书:
server {
listen 443 ssl;
server_name blog.example.com;
ssl_certificate /etc/nginx/certs/example.com/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/example.com/privkey.pem;
location / {
proxy_pass http://blog-app:8000;
}
}
注意这里写的是容器内路径。
验证
改完配置后,先检查:
docker exec global-nginx nginx -t
通过后再重载:
docker exec global-nginx nginx -s reload
自动续期也要确认。acme.sh 安装后通常会创建 cron 任务,可以查看:
crontab -l
也可以手动执行续期检查:
acme.sh --cron
必要时测试强制续期:
acme.sh --renew -d example.com --force
验证证书简答点直接打开浏览器
也可以用命令:
curl -I https://blog.example.com
openssl s_client -connect blog.example.com:443 -servername blog.example.com </dev/null 2>/dev/null | openssl x509 -noout -dates -subject -issuer
如果子域名正常、主域名异常,检查签发时是否包含 -d example.com。
如果主域名正常、子域名异常,检查证书是否包含 *.example.com。
小结:
这个方案的好处是:
- 多个子域名共用一套证书
- 证书路径固定
- 续期自动化
- 新增子域名时主要处理 DNS 和
Nginx配置 - 全局
Nginx容器架构一致
好处有,风险也很明确:
- 通配符证书私钥影响范围更大
- DNS API 凭据必须保护好
- 自动续期需要定期确认
reloadcmd必须正确*.example.com不覆盖无限层级子域名
到这里,SSL 证书管理从“手动配好一次”,推进到了“适合长期维护”。
通配符证书解决多子域名覆盖,DNS API 解决自动验证,acme.sh 解决签发、安装和续期,全局
Nginx负责统一入口和证书引用。
最后,感谢大佬开源的仓库 acme.sh
好像只有程序员这个行业,喜欢把产品无偿、开源,并写好教程供人使用。
为每一位不甘平凡、努力奋斗的程序员点赞!
下一篇,我会进入对象存储:为什么个人博客也需要对象存储?因为网站能安全访问之后,图片、附件和静态资源放在哪里,也会变成长期维护问题。
本文由 楸木 原创,转载请注明出处。