Let‘s Encrypt 证书到底会不会自动续期?从一次服务器迁移后的证书排查说起

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 这一环收尾。

至于历史证书,可以晚一点整理;失败日志,也可以慢慢清。

但正在使用的证书,一定要确保它今天能用,明天也能自己续上。

相关推荐
joinwell5216 分钟前
Agent 中断后,原任务如何安全接管?从恢复标记到效果事实
人工智能·后端·架构
qq_3391911438 分钟前
go cpu占比高排查,cpu100%排查,go pprof cpu命令
开发语言·后端·golang
JaguarJack1 小时前
现在就值得尝试的 5 个 Laravel 新包
后端·php·laravel·服务端
BingoGo1 小时前
现在就值得尝试的 5 个 Laravel 新包
后端·php
PBitW1 小时前
Travel Planner — 智能旅行行程规划助手
前端·后端
王川7081 小时前
Vue3 + uni-app 给商会小程序做一个动态表单引擎(
后端
烂蜻蜓2 小时前
Flask入门教程(二十四):请求对象API——获取客户端数据的完整指南
后端·python·flask
geovindu2 小时前
CSharp: Template Method Pattern
开发语言·后端·c#·.net·模板方法模式·行为模式
步行cgn2 小时前
Spring Boot application.properties 配置文件详解
后端