Let's Encrypt 证书到底会不会自动续期?从一次服务器迁移后的证书排查说起
做服务器迁移的时候,我们通常最关心的是应用能不能启动、数据库能不能连上、Nginx 转发是否正常、DNS 有没有切过去。
SSL 证书反而很容易被忽略。
原因也很简单:迁移完成以后打开网站,浏览器显示 HTTPS 正常,证书还有几十天才过期,看起来似乎没什么问题。 
但真正容易出问题的,往往不是"现在证书能不能用",而是:
两个月以后,这张证书还能不能自动续上?
最近整理一批迁移后的服务器时,就碰到了一个很典型的场景:服务器上部署了不少 Let's Encrypt 证书,有些域名已经迁移到了其他服务器,有些域名还在当前服务器运行,还有一些证书甚至已经过期。
表面上 HTTPS 都没有明显问题,但执行一次:
bash
certbot renew --dry-run
才发现一部分成功、一部分失败。
这篇文章就从这个场景出发,把 Let's Encrypt、Certbot、自动续期、Nginx、服务器迁移后的历史证书,以及第三方证书代理服务之间的关系一次讲清楚。
为了避免涉及真实业务信息,本文所有域名、IP、服务器名称均使用 Demo 数据。
一、Let's Encrypt 证书为什么只有几十天有效期?
假设我们访问:
text
https://agent.demo.com
查看证书信息发现:
text
颁发机构:Let's Encrypt
签发时间:2026-07-21
到期时间:2026-10-19
第一反应可能是:
为什么证书有效期这么短?以后是不是每隔几个月都要手工续?
其实不是。
Let's Encrypt 本身就是围绕自动化证书管理设计的。
典型架构是:
text
Let's Encrypt
│
│ ACME
↓
Certbot
│
↓
申请 / 验证 / 续期
│
↓
/etc/letsencrypt/live/
│
↓
Nginx
服务器上安装 Certbot 后,由 Certbot 和 Let's Encrypt 进行 ACME 通信。
第一次申请证书可能执行:
bash
certbot --nginx -d agent.demo.com
申请成功以后,证书通常保存在:
text
/etc/letsencrypt/live/agent.demo.com/fullchain.pem
/etc/letsencrypt/live/agent.demo.com/privkey.pem
Nginx 再引用:
nginx
ssl_certificate /etc/letsencrypt/live/agent.demo.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/agent.demo.com/privkey.pem;
后续并不需要管理员每隔几个月重新执行一遍申请命令。
正常情况下 Certbot 会自动续期。
二、怎么看服务器到底有没有配置自动续期?
只看浏览器里的证书信息,是无法判断服务器有没有配置自动续期的。
因为浏览器只能告诉我们:
text
当前证书:
有效
却无法告诉我们:
text
未来证书:
能不能续
真正应该登录服务器检查。
首先:
bash
certbot certificates
会看到类似:
text
Certificate Name: agent.demo.com
Domains: agent.demo.com agentadmin.demo.com
Expiry Date: 2026-10-19
Certificate Path: /etc/letsencrypt/live/agent.demo.com/fullchain.pem
Private Key Path: /etc/letsencrypt/live/agent.demo.com/privkey.pem
这说明证书已经被 Certbot 管理。
接下来查看自动任务:
bash
systemctl list-timers | grep certbot
不同 Linux 发行版或者不同 Certbot 安装方式,Timer 名字不一定完全一样。
可能是:
text
certbot.timer
也可能是:
text
certbot-renew.timer
所以如果:
bash
systemctl status certbot.timer
返回:
text
Unit certbot.timer could not be found.
先别急着认为自动续期坏了。
应该先:
bash
systemctl list-timers | grep certbot
确认实际运行的 Timer。
例如看到:
text
certbot-renew.timer certbot-renew.service
说明系统实际上已经存在 Certbot 定时续期任务。
三、判断自动续期是否正常,最重要的是这条命令
相比单纯检查 Timer,我更推荐:
bash
certbot renew --dry-run
这条命令非常重要。
它不是简单检查配置文件,而是模拟一次真正的续期流程。
如果输出类似:
text
The following simulated renewals succeeded:
/etc/letsencrypt/live/agent.demo.com/fullchain.pem (success)
/etc/letsencrypt/live/api.demo.com/fullchain.pem (success)
基本就可以确定:
text
Certbot 配置正常
+
域名验证正常
+
Let's Encrypt 可以验证
+
续期流程正常
也就是说:
这张证书未来具备正常自动续期的条件。
反过来,如果看到:
text
The following simulated renewals failed:
/etc/letsencrypt/live/old.demo.com/fullchain.pem (failure)
就需要继续调查。
四、为什么证书现在正常,自动续期却失败?
这是整个问题里最容易让人产生误解的地方。
假设:
text
api.demo.com
当前 HTTPS 完全正常。
证书还有 40 天才过期。
但是执行:
bash
certbot renew --dry-run
却出现:
text
Type: unauthorized
Invalid response from:
http://api.demo.com/.well-known/acme-challenge/xxxx
404
这并不矛盾。
因为"使用旧证书"和"申请新证书"是两件事情。
当前 HTTPS 使用的是已经存在服务器上的:
text
fullchain.pem
privkey.pem
所以只要这两个文件没有过期,网站就可以正常提供 HTTPS。
但是续期的时候,Let's Encrypt 需要重新确认:
你现在是否依然拥有这个域名的控制权?
于是会进行 ACME Challenge。
五、HTTP-01 Challenge 到底在干什么?
可以把它理解成一次"域名所有权考试"。
Certbot 告诉 Let's Encrypt:
text
我要给 api.demo.com 续证书。
Let's Encrypt 会要求:
text
请证明你控制 api.demo.com。
Certbot 临时准备一个验证文件,例如:
text
/.well-known/acme-challenge/abc123
然后 Let's Encrypt 从公网访问:
text
http://api.demo.com/.well-known/acme-challenge/abc123
如果能够读取到正确内容:
text
验证成功
↓
签发新证书
如果得到:
text
404
或者返回了网站首页 HTML:
html
<!doctype html>
<html>
...
那么 Let's Encrypt 会认为:
text
我没有拿到期望的验证文件
↓
无法证明服务器控制这个域名
↓
拒绝签发
最终就是:
text
unauthorized
六、服务器迁移为什么特别容易导致续期失败?
这次排查最典型的问题其实不是 Certbot,而是服务器迁移留下来的历史状态。
例如最开始:
text
api.demo.com
↓
DNS
↓
服务器 A
服务器 A 上有:
text
Nginx
Certbot
证书
renewal 配置
后来做了迁移:
text
api.demo.com
↓
DNS
↓
服务器 B
但是服务器 A 没有清理。
于是形成:
text
服务器 A
├── Nginx 配置还在
├── SSL 证书还在
├── Certbot renewal 配置还在
└── 自动续期 Timer 还在
服务器 B
├── 新 Nginx
├── 新证书
└── 新 Certbot
服务器 A 上的 Certbot 并不知道:
"业务已经迁移走了。"
它只能看到:
text
/etc/letsencrypt/renewal/api.demo.com.conf
于是认为:
"这是我要管理的证书。"
到了续期时间继续执行:
text
服务器 A Certbot
↓
请求续期 api.demo.com
↓
Let's Encrypt
↓
访问 api.demo.com
↓
DNS 指向服务器 B
↓
Challenge 文件不在服务器 B
↓
404
↓
续期失败
于是日志里开始不断出现:
text
unauthorized
404
challenge failed
renew failure
实际上业务可能一点问题都没有。
七、同一个域名,两台服务器都存在证书怎么办?
这也是迁移以后非常常见的现象。
例如服务器 A:
bash
certbot certificates
存在:
text
api.demo.com
服务器 B:
bash
certbot certificates
居然也存在:
text
api.demo.com
两台服务器同时保存同一个域名的证书文件,本身不一定马上造成事故。
真正的问题是:
到底应该由谁负责续期?
假设 DNS:
text
api.demo.com
↓
服务器 B
那么使用 HTTP-01 验证时,一般应该由服务器 B 负责续期。
理想状态应该是:
text
api.demo.com
↓
服务器 B
↓
Certbot
↓
自动续期
而不是:
text
┌→ 服务器 A Certbot
api.demo.com ──┤
└→ 服务器 B Certbot
否则后期排查问题会越来越乱。
八、历史证书要不要删除?
答案是:
建议最终清理,但不用看到历史证书就立刻删除。
比如服务器迁移以后发现:
text
ai.old-demo.com
api.old-demo.com
admin.old-demo.com
已经完全不属于当前服务器。
理论上当然应该清掉。
但是千万不要直接:
bash
rm -rf /etc/letsencrypt/live/xxx
Certbot 的证书目录之间存在关联关系,手工删除很容易把 Certbot 状态弄乱。
正确方式是:
bash
certbot delete --cert-name ai.old-demo.com
但在执行之前还有一个更重要的检查:
bash
nginx -T 2>/dev/null | grep -E "server_name|ssl_certificate"
因为很可能出现:
nginx
server_name ai.old-demo.com;
ssl_certificate /etc/letsencrypt/live/ai.old-demo.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ai.old-demo.com/privkey.pem;
也就是说:
业务虽然迁走了,但旧 Nginx 配置还在引用证书。
这时候直接:
bash
certbot delete --cert-name ai.old-demo.com
可能导致证书文件消失。
当前已经启动的 Nginx 进程未必马上出问题,但下一次:
bash
nginx -t
或者:
bash
systemctl restart nginx
就可能因为找不到证书文件直接报错。
所以正确清理顺序应该是:
text
确认域名已经迁移
↓
确认 DNS 不再指向旧服务器
↓
删除/禁用旧 Nginx Server 配置
↓
nginx -t
↓
reload nginx
↓
确认服务正常
↓
certbot delete
↓
certbot renew --dry-run
这个顺序非常重要。
九、历史证书暂时不删除行不行?
可以。
如果当前服务器上还有一些已经迁走的证书:
text
ai.demo-a.com
ai.demo-b.com
ai.demo-c.com
暂时保留通常不会直接影响正在运行的网站。
最大的副作用是:
bash
certbot renew
会继续尝试续这些证书。
于是最后可能看到:
text
success
success
failure
failure
failure
这里还有一个值得注意的地方:
部分证书续期失败,并不意味着所有证书都会续期失败。
Certbot 会分别处理不同证书。
所以可能出现:
text
Certificate A success
Certificate B success
Certificate C failure
Certificate D failure
A、B 仍然可以正常续。
问题主要是日志会越来越乱。
真正需要续期的证书如果某一天也失败了,很容易淹没在大量历史失败日志里面。
所以短期:
可以不删。
长期:
建议清理。
十、一个 Certificate Name 可能对应多个域名
这是清理时另一个非常容易踩坑的地方。
例如:
text
Certificate Name: ai.demo.com
Domains:
ai.demo.com
aicontrol.demo.com
apis.demo.com
这实际上是一张 SAN 多域名证书。
执行:
bash
certbot delete --cert-name ai.demo.com
删除的是整张证书。
不是只删除:
text
ai.demo.com
而保留:
text
aicontrol.demo.com
apis.demo.com
因此删除之前必须看:
bash
certbot certificates
确认:
text
Certificate Name
Domains
这两个字段。
十一、迁移后最危险的不是"没用的证书",而是"需要的证书续不上"
历史证书续期失败,其实还不是最危险的。
例如:
text
old.demo.com
业务早就迁走了。
它:
text
renew failure
问题不大。
真正应该优先关注的是:
text
api.demo.com
业务现在还跑在这台服务器。
但是:
bash
certbot renew --dry-run
失败了。
这种情况意味着:
text
现在:
HTTPS 正常
30 天后:
可能正常
60 天后:
证书可能过期
某一天:
浏览器开始报证书错误
API 客户端 TLS 握手失败
所以检查 Certbot 时,应该把证书分成三类:
| 类型 | 当前状态 | 处理优先级 |
|---|---|---|
| 正在使用 + dry-run 成功 | 正常 | 低 |
| 已迁移 + dry-run 失败 | 历史垃圾 | 中 |
| 正在使用 + dry-run 失败 | 潜在事故 | 最高 |
这个分类比单纯看到:
text
5 renew failure(s)
就开始紧张,更有意义。
十二、第三方"证书发放代理机构"跟 Certbot 有什么区别?
排查到这里,又引出了另一个问题:
为什么不直接找第三方证书服务商?让他们负责证书不就好了?
这需要先区分几个角色。
Let's Encrypt 本身属于 CA。
Certbot 是 ACME 客户端。
你当前的模式可以理解成:
text
Let's Encrypt
↓
Certbot
↓
你的服务器
↓
Nginx
而第三方证书代理/托管平台通常是:
text
你
↓
第三方证书管理平台
↓
CA
↓
申请 / 托管 / 续期 / 下发
↓
服务器 / CDN / LB
所以两者最大的区别并不是:
谁的 HTTPS 更安全?
而是:
谁负责证书生命周期管理。
十三、Let's Encrypt + Certbot 的优势
对于普通服务器,我个人仍然很喜欢这套方案。
第一,成本低。
Let's Encrypt 免费,对大量普通 HTTPS 网站、后台、API 服务来说非常合适。
第二,自动化程度很高。
正常情况下:
bash
certbot renew
就可以统一处理。
再配合:
text
systemd timer
基本可以做到无人值守。
第三,和 Nginx 集成方便。
例如:
bash
certbot --nginx -d api.demo.com
Certbot 可以直接协助完成验证和部署。
第四,控制权高。
证书文件、私钥、续期配置都掌握在自己的服务器环境里。
第五,厂商绑定比较弱。
换服务器、换云厂商都可以继续使用 ACME 体系。
十四、Let's Encrypt + Certbot 的缺点也很明显
最大的问题就是:
你自己就是证书运维人员。
DNS 改了,你要知道。
Nginx 改了,你要知道。
服务器迁移了,你要处理 Certbot。
Challenge 被代理层拦截了,你要自己排查。
证书续期失败了,也需要自己发现。
当环境只有:
text
2 台服务器
10 个域名
问题不大。
如果变成:
text
50 台服务器
300 个域名
多个 K8s 集群
多个 CDN
多个负载均衡器
多个云厂商
继续让每台服务器各自跑 Certbot,管理复杂度会迅速上升。
十五、第三方证书托管平台的真正优势是什么?
第三方平台最大的价值通常不是:
"我们的证书加密能力比 Let's Encrypt 强。"
而是:
统一管理。
理想情况下:
text
统一证书管理平台
│
┌──────────────┼──────────────┐
↓ ↓ ↓
Nginx K8s Ingress CDN/LB
↓ ↓ ↓
Server A Cluster A Cloud
平台统一完成:
text
申请
↓
域名验证
↓
签发
↓
部署
↓
续期
↓
重新部署
↓
到期监控
↓
失败告警
对于大型环境,这个优势非常明显。
十六、收费证书是不是一定比免费证书安全?
这个问题也经常被误解。
很多人看到:
text
免费 SSL
下意识觉得:
text
安全性一般
看到:
text
几千元 SSL
就觉得:
text
加密肯定更强
实际上不能这么简单理解。
HTTPS 的安全性涉及:
text
TLS 协议版本
密钥类型
密钥长度
密码套件
服务器配置
私钥保护
客户端兼容性
证书验证
而不是简单由:
text
证书价格
决定。
对于普通:
text
https://api.demo.com
Let's Encrypt 提供的 DV 证书在很多业务场景已经完全够用。
商业 CA 的价值更多可能体现在:
text
企业级证书管理
组织验证
商业支持
SLA
审计要求
集中管理
特定兼容性
所以不能简单总结成:
text
免费 = 不安全
收费 = 更安全
这种理解是不准确的。
十七、什么时候值得从 Certbot 升级到统一证书平台?
如果环境还是:
text
几台服务器
几十个域名
Nginx 为主
我认为:
text
Let's Encrypt
+
Certbot
+
systemd timer
+
证书监控
已经足够稳定。
但是如果环境逐渐发展成:
text
大量服务器
+
大量域名
+
Kubernetes
+
CDN
+
SLB
+
多云
+
频繁迁移
就应该认真考虑统一证书管理。
因为此时最大的问题已经不是:
能不能申请证书?
而是:
我怎么知道每张证书在哪里、谁负责续、什么时候过期、部署到了哪些节点?
这其实已经从"SSL 配置问题"升级成了"基础设施资产管理问题"。
十八、我现在更推荐的一套证书管理规范
经历这次排查以后,我认为至少应该建立下面几个基本规则。
第一,一个域名明确一个证书续期责任节点。
不要服务器 A、B、C 都认为:
text
api.demo.com
应该由自己续。
第二,每次服务器迁移必须把 SSL 放进迁移 Checklist。
不能只检查:
text
应用
数据库
Redis
MQ
Nginx
DNS
还应该检查:
text
Certbot
SSL
ACME
renewal
第三,迁移完成以后必须执行:
bash
certbot renew --dry-run
而不是看到浏览器 HTTPS 正常就宣布迁移结束。
第四,旧服务器及时清理 Nginx 和 Certbot 历史配置。
但一定遵循:
text
Nginx 引用解除
↓
nginx -t
↓
reload
↓
Certbot delete
不要反过来。
第五,做证书到期监控。
哪怕 Certbot 已经自动续期,也不要完全相信:
text
"自动化配置过,所以以后一定不会出问题。"
DNS、Nginx、CDN、WAF、服务器、网络策略都可能发生变化。
自动续期解决的是:
自动执行。
监控解决的是:
自动执行失败以后,有没有人知道。
这两个东西缺一不可。
十九、几个非常实用的 Certbot 排查命令
最后整理几个日常最常用的。
查看 Certbot 管理的所有证书:
bash
certbot certificates
查看自动续期任务:
bash
systemctl list-timers | grep certbot
模拟续期:
bash
certbot renew --dry-run
单独处理某张证书:
bash
certbot renew --cert-name api.demo.com
确认需要强制重新申请时:
bash
certbot renew --cert-name api.demo.com --force-renewal
查看 Nginx 当前到底引用了哪些证书:
bash
nginx -T 2>/dev/null | grep -E "server_name|ssl_certificate"
检查某个域名:
bash
nginx -T 2>/dev/null | grep -n -A 30 -B 10 "api.demo.com"
检查 Nginx:
bash
nginx -t
平滑重新加载:
bash
systemctl reload nginx
删除确认废弃的 Certbot 证书:
bash
certbot delete --cert-name old.demo.com
查看线上实际返回的证书:
bash
echo | openssl s_client \
-connect api.demo.com:443 \
-servername api.demo.com 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates
基本掌握这些命令以后,大部分 Let's Encrypt + Nginx 证书问题都可以快速定位。
二十、写在最后:证书没过期,不代表证书系统是健康的
这次排查最值得记住的其实不是某条 Certbot 命令,而是一个很容易被忽略的运维思维:
"现在能用"和"未来能够持续正常运行"是两回事。
浏览器告诉我们:
text
HTTPS 正常
只能证明当前证书有效。
而:
bash
certbot renew --dry-run
验证的则是:
text
DNS
+
ACME
+
Nginx
+
Certbot
+
Let's Encrypt
这一整条未来续期链路是否仍然畅通。
服务器迁移以后尤其应该做一次这样的检查。
因为服务器迁移并不是:
text
代码复制过去
+
DNS 切过去
=
结束
真正完整的迁移应该包括:
text
应用
数据库
缓存
消息队列
文件
DNS
Nginx
SSL
Certbot
监控
定时任务
日志
备份
证书只是其中一个很小的环节,但它有一个很麻烦的特点:
配置错了以后,往往不会立即出问题,而是在几十天之后突然爆炸。
这也是为什么很多线上 SSL 事故看起来都很奇怪:
"昨天还好好的,怎么今天突然所有客户端都连不上了?"
因为真正的问题可能两个月前迁移服务器的时候就已经埋下了。
所以以后服务器迁移完成,我建议固定补上最后两个动作:
bash
nginx -t
certbot renew --dry-run
如果所有正在使用的证书都能看到:
text
success
这个迁移才算真正把 HTTPS 这一环收尾。
至于历史证书,可以晚一点整理;失败日志,也可以慢慢清。
但正在使用的证书,一定要确保它今天能用,明天也能自己续上。