证书手工换了好几年,2017 年那张值班表、2019 年凌晨两点那次,都是这么来的。2025 年春天终于改成自动续期。第一次没跑通,踩了三处。
1. 验证目录被 Nginx 挡了
ACME 校验要在 /.well-known/acme-challenge/ 下放一个临时文件。我们 Nginx 配置里有一条把点开头的路径全 return 404,正好把验证请求也挡了,签发一直报 403。
加了一条 location 精确匹配那个目录,指向一个真实存在的 webroot,别的点路径照旧拒。
2. 续上了但没生效
证书文件更新了,Nginx 没重载,浏览器拿到的还是旧的。第一版脚本里压根没写 reload。
改成把 reload 放进续期工具的 post-hook,只有真的签发了新证书才执行,不每次定时都 reload 一遍。另外 hook 里加了一句先 nginx -t 验配置,配置错了就别 reload,免得把站搞挂。
3. 跑没跑、成没成,没人知道
定时任务默认输出发到某个邮箱,跟 2019 年那个坑一模一样。我们改成脚本每次跑完往一张 cert_log 表写一行:域名、执行时间、结果、新到期日。失败单独标出来,接进那个只有一个群的告警。
另外留了个独立的小检查,跟续期脚本分开:每天读一次证书实际到期日,30 天、7 天各校一次,跟表里记的对不上就报。不依赖续期工具自己说成功。
4. 别把多个域名捆一张证书
第一版图省事,把 6 个域名签在一张证书里。后来有个域名要下架,重新签发时另外 5 个跟着一起动,风险摊大了。拆成按域名各一张,脚本循环跑,慢一点,但哪个出问题不连累别的。
现在
跑了半年多,自动续了 11 次,人工只介入过 1 次,是一个域名的 DNS 解析临时坏了导致校验失败,修完重跑就好。
那张 2019 年手填的证书表没删,导出成 CSV 存着。字段还是四个:域名、到期日、存放路径、负责人。只是现在到期日那列由脚本自己写了。

