在 Ubuntu 22.04 上给站点配 HTTPS,绕不开 Certbot。它几乎是 Let‘s Encrypt 免费证书的事实标准客户端,一条命令就能申请、续期、自动部署证书。但很多人一开始只知道 certbot --nginx 这种全自动玩法,真到了 Nginx 没装、要签泛域名、或者端口被占用的场景,就卡住了。这篇文章我把 Certbot 申请证书的三种主流方式——Standalone、Webroot、DNS Challenge——从头到尾捋一遍,包括适用场景、实际操作、续期配置和我踩过的坑。目标读者是刚接触 Linux 服务器、或者折腾过证书但没系统梳理过原理的朋友。
用 Certbot 申请证书这件事,表面看是“敲几条命令”,实际上背后是 ACME 协议、挑战(Challenge)机制、端口/文件/DNS 权限这三类资源的使用逻辑。把这层逻辑想清楚,后面无论换云服务器、换域名商还是换 Web 服务器,你都能很快上手。
1. 内容整体设计与思路拆解
1.1 为什么是 Certbot 而不是手动上传证书
常见的免费证书申请渠道有两种:一是阿里云、腾讯云这类云平台的控制台里直接申请免费证书,二是用 Certbot 这类 ACME 客户端自动申请。前者胜在零门槛,浏览器里点几下就行,但往往需要手动下载证书文件、上传到服务器、再改 Web 服务器配置,而且免费证书的有效期通常是 3 个月到 1 年,到期后你又要手动来一轮。
Certbot 解决的是“自动化”问题。它通过 ACME 协议与 Let’s Encrypt 的服务器通信,完成域名所有权验证后,直接把证书文件写到服务器上,还能自动修改 Nginx / Apache 配置,并通过 systemd timer 或 cron 定期检查续期。等于说,只要配置好一次,后续基本不用管。
有人问:Let‘s Encrypt 证书有效期只有 90 天,太短了吧?其实这是故意的。短有效期可以倒逼自动化,也能在密钥泄露时更快轮换。只要续期链路是通的,90 天和 365 天对你来说没有任何区别。
1.2 三种挑战方式的底层原理
ACME 协议的核心是“证明你拥有这个域名”。Certbot 提供了多种挑战方式,但最常用来申请证书的就三种:
- HTTP-01 挑战(Standalone / Webroot):Let‘s Encrypt 会访问
http://你的域名/.well-known/acme-challenge/xxx,你在服务器上放一个指定内容的文件,能访问到就证明你拥有域名控制权。Standalone 是 Certbot 自己临时起一个 80 端口的 Web 服务来响应;Webroot 则是把验证文件写到你的站点根目录,由你现有的 Web 服务器来响应。 - DNS-01 挑战:Let’s Encrypt 要求你在域名的 DNS 解析记录里添加一条 TXT 记录,记录值由 Certbot 生成。能添加这条记录,说明你有 DNS 管理权限。这种方式最大的优势是可以用在 80/443 端口都被占用、服务器没有公网 IP、或者要申请泛域名证书的场景。
一句话总结:Standalone 需要你暂时让出 80 端口,Webroot 需要你有一个正在运行的 Web 服务和可写的站点目录,DNS Challenge 需要你能操作 DNS 解析记录。三者不需要纠结谁更“高级”,只看哪个适合你的现状。
1.3 选型判断标准
我的建议很简单:
- 服务器上除了 Nginx 没跑别的东西,而且暂时可以接受 80 端口短暂被占用,用 Standalone,因为它依赖最少。
- 服务器上已经跑着 Nginx/Apache,网站也有实际的站点目录,用 Webroot,因为不需要停服务,也不会和现有端口冲突。
- 要签泛域名
*.example.com,或者服务器没有对外的 80 端口,或者不方便开放防火墙端口,用 DNS Challenge。如果能用云服务商的 API 插件实现全自动,体验最好。
后面几节我按这三种方式,分别给出 Ubuntu 22.04 下的完整操作。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与 Certbot 安装
2.1 Ubuntu 22.04 环境检查
动手前先确认系统版本、时间和网络。Let‘s Encrypt 的 ACME 服务对客户端时间很敏感,系统时间不准会导致签名校验失败,所以第一步是检查时间同步:
bash复制timedatectl
看到 System clock synchronized: yes 就正常。如果显示 no,先安装并启用时间同步:
bash复制sudo apt update
sudo apt install -y systemd-timesyncd
sudo timedatectl set-ntp true
接着确认系统版本:
bash复制cat /etc/os-release
只要是 22.04,Ubuntu 官方源里就有 certbot,不需要额外加第三方 PPA。
然后确认你准备申请证书的域名已经解析到当前服务器的公网 IP:
bash复制dig +short example.com
如果你本机没有 dig,先装 dnsutils。
2.2 安装 Certbot 与插件
Ubuntu 22.04 的 apt 源里 Certbot 版本比较新,直接装即可。要注意的是,不同插件对应不同的 Web 服务器,官方推荐安装你需要的插件,而不是全都装:
bash复制sudo apt update
sudo apt install -y certbot
如果你用 Nginx,可以装 nginx 插件:
bash复制sudo apt install -y python3-certbot-nginx
如果考虑用系统自带包管理方式续期,这一步就够了。我个人的建议是优先用系统包安装,因为 apt 升级可以连依赖一起管,和 Ubuntu 的整体安全更新节奏一致。某些教程会推荐用 snap 安装,但 Ubuntu 22.04 用 apt 装的 certbot 已经足够稳定,没必要额外引入一套包管理机制。
2.3 确认版本与帮助信息
安装完先看一眼版本:
bash复制certbot --version
正常会输出类似 certbot 2.x.x 的信息。再确认帮助里能看到的插件列表:
bash复制certbot plugins
这一步主要确认 nginx 插件是否可用,以及后面 DNS 插件是否需要单独装。比如用 Cloudflare 做 DNS 解析的话,要装对应的 python3-certbot-dns-cloudflare,用阿里云解析就需要第三方插件,这个后面单独说。
3. 方式一:Standalone 模式申请证书
3.1 适用场景
Standalone 模式是 Certbot 最“原教旨”的用法。它不依赖现有 Web 服务器,Certbot 自己拉起一个临时的 HTTP 服务来响应挑战。最典型的使用场景是:
- 服务器上还没有安装任何 Web 服务器。
- 只是想给某个内部服务或者 API 网关签一张证书,Nginx 还没配好。
- 测试环境,想快速验证证书申请链路是否通。
它的核心要求是:80 端口必须空闲。因为 Let‘s Encrypt 的 HTTP-01 挑战固定走 80 端口,如果 80 端口已经被 Nginx 或别的进程占用,Certbot 就会报 Bind for 0.0.0.0:80 failed: port is already in use。
3.2 申请证书的操作命令
先安装 Certbot(如果还没装):
bash复制sudo apt update
sudo apt install -y certbot
然后执行:
bash复制sudo certbot certonly --standalone -d example.com -d www.example.com --email you@example.com --agree-tos --no-eff-email
逐项说下这些参数:
certonly:只获取证书,不自动修改 Web 服务器配置。这个参数我建议始终加上,因为自动改 Nginx 配置很方便,但有时候会破坏你已有的多站点配置,手动改更可控。--standalone:使用 standalone 验证方式。-d example.com -d www.example.com:可以写多个域名,第一次申请时 Certbot 会把它们放在同一张证书里。--email:用于接收过期提醒和紧急通知。--agree-tos:同意 Let’s Encrypt 订阅协议。--no-eff-email:不订阅 EFF 基金会的邮件。
执行成功后,证书会存放在 /etc/letsencrypt/live/example.com/ 目录下,里面有四个文件:
cert.pem:服务器证书。chain.pem:中间证书链。fullchain.pem:服务器证书 + 中间证书链,Nginx 配置里最常用。privkey.pem:私钥,注意权限,尽量只让 root 和 Web 服务器可读。
3.3 端口占用与防火墙处理
Standalone 模式最容易踩的坑就是端口被占用。如果你用 Nginx 跑着 80 端口,要么先临时停掉:
bash复制sudo systemctl stop nginx
申请成功后记得再启动:
bash复制sudo systemctl start nginx
要么干脆换 Webroot 模式,后面会讲。
另外还要检查云服务器的安全组和本机防火墙。Ubuntu 22.04 默认没有启用 ufw,但如果你开过:
bash复制sudo ufw status
如果状态是 active,确认 80 端口允许:
bash复制sudo ufw allow 80/tcp
如果你是刚配置完域名解析,记得先确认解析生效再执行 Certbot,否则挑战会失败。用 ping 或 curl -I http://example.com 验证一下。
3.4 证书自动续期说明
注意,Standalone 模式申请到的证书,续期时同样会占用 80 端口。如果你后续用 Nginx 跑着网站,Certbot 默认通过 systemd timer 执行的续期任务可能会因为端口冲突而失败。
解决办法有两个:
- 在续期命令中加入
--pre-hook "systemctl stop nginx" --post-hook "systemctl start nginx",让 Certbot 在续期前停掉 Nginx、续期后再启动。 - 更稳妥的做法是,如果 Nginx 已经长期在跑,干脆切换到 Webroot 模式重新申请一次证书,以后就没有端口冲突的问题。
在 Ubuntu 22.04 上,安装了 certbot 之后会自动生成一个 certbot.timer,激活的定时任务默认每天执行两次续期检查。这个稍后统一讲。
4. 方式二:Webroot 模式申请证书
4.1 适用场景
Webroot 模式是我在生产环境最推荐的方式。它不占用新的端口,也不需要停 Web 服务,而是把验证文件写到你的站点根目录下的一个临时目录里,借助现有 Web 服务器来响应 Let‘s Encrypt 的验证请求。
适用场景:
- 服务器上已经跑着 Nginx/Apache,网站正常对外服务。
- 80 端口不能被占用,想避免 Standalone 模式的停机窗口。
- 一台服务器上跑了多个站点,想分别给不同域名申请证书。
4.2 配置 Nginx 服务器块
Webroot 模式要求你的 Web 服务器能正确处理 /.well-known/acme-challenge/ 路径的请求。这里有一个关键点:有些站点的根目录在项目代码目录里,有些配置了 alias 或者 rewrite,可能导致 Certbot 写入的验证文件无法被 Let’s Encrypt 访问到。最干净的做法是为验证路径单独创建一个目录,并在 Nginx 里做一个专门的位置块。
假设你的站点根目录是 /var/www/example.com,先在根目录下创建验证目录:
bash复制sudo mkdir -p /var/www/example.com/.well-known/acme-challenge
然后在 Nginx 的 server 块里加上:
nginx复制server {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com;
location ^~ /.well-known/acme-challenge/ {
allow all;
default_type "text/plain";
}
# 其他配置...
}
这里用 ^~ 是为了让这个 location 优先匹配,避免被其他 rewrite 或 location 规则拦截。保存后测试配置并重载:
bash复制sudo nginx -t
sudo systemctl reload nginx
这一步很关键,如果 Nginx 配置有问题,后面 Certbot 挑战文件写到目录里也没用,因为外面访问不到。
4.3 申请证书的操作命令
Webroot 模式的命令如下:
bash复制sudo certbot certonly --webroot -w /var/www/example.com -d example.com -d www.example.com --email you@example.com --agree-tos --no-eff-email
核心参数:
--webroot:使用 webroot 验证方式。-w /var/www/example.com:指定验证文件的写入目录。如果申请多个域名,而每个域名的站点根目录不同,可以多次使用-w。比如:
bash复制sudo certbot certonly --webroot \
-w /var/www/site1 -d site1.example.com \
-w /var/www/site2 -d site2.example.com
执行之后,Certbot 会在每个 -w 指定的目录下创建 .well-known/acme-challenge/ 目录,写入临时验证文件,等待 Let‘s Encrypt 服务器访问。验证通过后,证书文件同样会写到 /etc/letsencrypt/live/你的域名/ 下。
4.4 权限与根目录注意事项
Webroot 模式最大的坑是权限问题。Certbot 默认以 root 身份写入验证文件,所以理论上不会遇到写不了的问题。但如果你用 Docker 部署的 Nginx,或者站点目录的所有者和运行用户是 www-data,就需要确保 Certbot 写入的文件可以被 Nginx 进程读取。一般目录权限设置为 755、文件设置为 644 就没问题。
另外,如果你把站点根目录放在 /usr/share/nginx/html,而里面已经有了一个 .well-known 目录,比如一些第三方服务(如 Google Search Console)要求放验证文件,注意别搞混淆。Certbot 会在 .well-known/acme-challenge 子目录里写文件,不会覆盖其他文件,但你手动创建目录时要注意属主。
排查思路很简单:申请失败时,先看提示是什么原因。如果提示 404,说明 Let‘s Encrypt 无法通过 HTTP 访问到验证文件。你可以先在浏览器或服务器上自己测试:
bash复制curl -I http://example.com/.well-known/acme-challenge/test
如果返回 403/404,那和 Certbot 无关,是你 Nginx 配置或路径映射的问题。
5. 方式三:DNS Challenge 申请证书
5.1 适用场景
DNS Challenge 和前两种最大的区别是:它不经过 80 端口,也不依赖 Web 服务器。Let‘s Encrypt 会要求你在域名的 DNS 解析里加一条 TXT 记录,你添加成功后,CA 服务器会在全球多个节点查询这条记录,确认你对域名的控制权。
适用场景:
- 需要申请泛域名证书,比如
*.example.com。 - 服务器没有公网 IP,或者 80/443 端口被防火墙/运营商封禁。
- 不想让 Certbot 和现有 Web 服务器产生任何交互。
- 内网穿透场景,通过公网 IP 无法直接访问服务器。
DNS Challenge 分为手动和自动两种实现。手动方式是 Certbot 给你一段 TXT 记录值,你登录域名解析平台自己添加;自动方式是通过 DNS 服务商的 API,让 Certbot 自动添加和删除 TXT 记录。
5.2 DNS 提供商与插件准备
自动方式需要知道你的域名解析服务商。国外比较常见的是 Cloudflare、Route53、DigitalOcean,这些官方都有对应的 Certbot 插件,直接 apt 安装即可。国内服务商比如阿里云、腾讯云,官方没有提供 Certbot 插件,但有社区维护的第三方插件,比如 certbot-dns-aliyun,用法和官方插件类似,只是依赖 Python 环境的配置稍微繁琐一些。
这里我用国内用户常见的 Cloudflare 作为例子。首先安装插件:
bash复制sudo apt install -y python3-certbot-dns-cloudflare
然后创建 API 凭证文件。在 Cloudflare 后台申请一个 API Token,权限只需要 Zone.DNS:Edit,然后在服务器上创建文件:
bash复制sudo mkdir -p /etc/letsencrypt
sudo nano /etc/letsencrypt/cloudflare.ini
内容写:
ini复制dns_cloudflare_api_token = 你的_Token
注意,这个文件包含了 API 凭证,必须收紧权限:
bash复制sudo chmod 600 /etc/letsencrypt/cloudflare.ini
sudo chown root:root /etc/letsencrypt/cloudflare.ini
5.3 手动 DNS 模式
因为自动 DNS 插件依赖你的 DNS 服务商,不一定人人适用,所以先讲手动模式。
执行:
bash复制sudo certbot certonly --manual --preferred-challenges dns \
-d example.com -d '*.example.com' \
--email you@example.com --agree-tos --no-eff-email
注意这里 -d '*.example.com' 的引号不能少,否则 shell 会把 *.example.com 展开成当前目录下匹配的文件名,导致命令出错。
执行后 Certbot 会提示你添加一条 TXT 记录,类似:
code复制_acme-challenge.example.com. TXT "xxxx-xxxx-xxxx"
你去 DNS 解析平台添加这条记录,等它生效后再按回车继续。TTL 建议设置短一点,比如 300 秒,这样生效更快,不用干等很久。
这里有一个常见的理解偏差:DNS Challenge 申请证书并不要求你手动添加两条 TXT 记录,除非一张证书里包含多个域名,或者包含两个级别的域名(比如 example.com 和 *.example.com),Certbot 可能要求你分别验证两个域名所需的 TXT 记录。仔细看终端提示,按提示操作就行。
5.4 自动 DNS 插件模式的完整流程
手动模式最大的问题在于续期不方便。90 天到期后,你又要手工登录 DNS 平台改 TXT 记录,这违背了自动化的初衷。如果你能用 API 插件,体验会好非常多。
以 Cloudflare 为例,凭证文件创建好之后,执行:
bash复制sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
-d example.com -d '*.example.com' \
--email you@example.com --agree-tos --no-eff-email
后面申请泛域名证书时,-d '*.example.com' 是必须的。很多新手在这里会漏掉根域名 example.com,导致泛域名证书只覆盖二级子域名,却没法覆盖裸域。如果想让 example.com 和 *.example.com 都能用 HTTPS,这两个域名要同时放进同一张证书里,也就是上面这条命令的写法。
申请成功后,续期也是全自动的。Certbot 的定时任务会自动检测证书剩余有效期,未来某天你的泛域名证书快到期时,它会自动调用 DNS API 添加临时 TXT 记录、完成验证、清理记录,整个过程不会产生任何人工干预。
5.5 泛域名证书的几个坑
泛域名证书虽然方便,但有几个坑必须提前知道:
- 泛域名证书不包含裸域。
*.example.com只能匹配www.example.com、api.example.com等子域名,不能匹配example.com,需要的话把裸域也加进同一张证书。 - 泛域名匹配不到多级子域名。
*.example.com只能匹配一级子域名,a.b.example.com不在覆盖范围内。 - TXT 记录生效有延迟。自动模式下 Certbot 会在验证完成后自动删除 TXT 记录,但 DNS 缓存可能导致记录残留一段时间,如果你手动验证,建议等 1 到 5 分钟。
我自己的经验是,泛域名证书适合“域名下有很多子服务、且子域名频繁变化”的场景。如果只有一个站,普通单域名证书就够用,没必要为了泛域名增加 DNS 插件的复杂度。
6. 证书续期与自动化
6.1 续期原理与有效期检查
Let‘s Encrypt 证书有效期是 90 天,但要记住:Certbot 不能等证书过期了才续期,它默认会在剩余有效期小于 30 天时发起续期。也就是说,一张刚签发的证书,大概在 60 天后就会被自动续期。
Ubuntu 22.04 上通过 apt 安装 certbot,会自动创建 certbot.timer 和配套的 service,你不需要自己写 cron。检查一下这个定时器是否在运行:
bash复制systemctl list-timers | grep certbot
正常会看到类似:
code复制certbot.timer ... left ...
如果没有输出,说明定时器没启用,手动开启:
bash复制sudo systemctl enable --now certbot.timer
6.2 验证续期是否正常
不要等到证书真到期了才发现续期链路有问题。装完证书后,先做一次续期演练:
bash复制sudo certbot renew --dry-run
这个命令会真实地走一次续期流程,但不会替换现有证书。只要命令结束输出 Congratulations, all simulated renewals succeeded,说明续期链路是通的。
如果用了 Standalone 模式申请证书,而 Nginx 一直占着 80 端口,--dry-run 会失败。这种情况需要在 /etc/letsencrypt/cli.ini 或续期配置里加上 pre-hook 和 post-hook,或者改用 Webroot 模式。
6.3 证书过期时间查看方法
经常有人问怎么判断证书何时到期。最直接的方法是用 openssl:
bash复制sudo openssl x509 -enddate -noout -in /etc/letsencrypt/live/example.com/cert.pem
输出类似:
code复制notAfter=Jun 10 12:00:00 2025 GMT
如果需要批量检查服务器上所有证书的剩余天数,可以写个小循环脚本:
bash复制for cert in /etc/letsencrypt/live/*/cert.pem; do
echo "$cert: $(openssl x509 -enddate -noout -in $cert)"
done
这个方法同样适用于检查其他来源的证书,比如 Nginx 上直接配置的第三方证书,路径换成你自己的证书文件位置即可。
6.4 续期后证书自动生效
Certbot 续期成功后,新的证书文件会直接覆盖 /etc/letsencrypt/live/example.com/ 下的 cert.pem、privkey.pem 等软链接指向的文件。但这里有个关键点:Nginx 不会自动重新加载证书。如果你不重载 Nginx,它还会继续使用内存中缓存的旧证书,直到到期或被手动重载。
解决方法是在续期命令里加一个 --deploy-hook,让 Certbot 在成功续期后自动重载 Nginx。修改 /etc/letsencrypt/cli.ini:
ini复制deploy-hook = systemctl reload nginx
然后手动触发一次续期测试:
bash复制sudo certbot renew --dry-run
如果一切正常,下次实际续期后 Nginx 会自动 reload,新证书立即生效。
7. 常见问题与排查技巧实录
7.1 Standalone 模式报“port is already in use”
这是 Standalone 模式最常见的报错。解决办法:
bash复制sudo ss -tlnp | grep :80
先看 80 端口被谁占用。如果是 Nginx,临时停掉再申请;如果是别的服务,要么换 Webroot,要么给 Certbot 指定 --http-01-port 换个端口(但这时候需要在 Nginx 里把该端口的请求转发到验证路径,不推荐新手折腾)。
7.2 Webroot 模式报 404
Webroot 模式申请失败,报错提示 Invalid response from http://example.com/.well-known/acme-challenge/...,大概率是 Nginx 配置没有正确处理验证路径。
排查顺序:
- 检查验证目录是否存在:
ls /var/www/example.com/.well-known/acme-challenge/。 - 检查 Nginx location 规则是否匹配该路径。
- 检查是否被重定向到 HTTPS:如果 80 端口配置了
return 301 https://...,验证请求也会被跳走,需要在 location 块里单独放行。
第三个坑最常见。很多 Nginx 配置会把所有 HTTP 请求 301 到 HTTPS,结果 Let‘s Encrypt 请求验证文件时也被跳转了。正确的配置是在 server 块中,先单独匹配 /.well-known/acme-challenge/ 路径并返回 200,再写其他跳转规则。
7.3 DNS Challenge 手动验证不通过
如果你按 Certbot 提示添加了 TXT 记录,但它一直提示验证失败,先不要急着重新申请。用 dig 手动查一下记录是否生效:
bash复制dig @8.8.8.8 TXT _acme-challenge.example.com
有时本地 DNS 缓存会干扰判断,指定公共 DNS 服务器查询更准确。如果查询结果显示记录已生效,但 Certbot 仍然失败,常见原因是你给多个域名申请证书,但只添加了其中一个域名的 TXT 记录。仔细看 Certbot 终端的提示,它会在需要你添加记录时明确列出域名。
另外注意,TXT 记录值可能包含引号。复制粘贴时不要多出空格,有些 DNS 平台会自动把整条记录加上引号,这可能导致记录值变长不匹配,需要检查平台显示的是否和 Certbot 提示的一字不差。
7.4 证书装好了但浏览器不认账
申请成功后,关键的一步是把证书配置到 Web 服务器。Nginx 最标准的写法是:
nginx复制server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
}
注意,ssl_certificate 一定要用 fullchain.pem,而不是 cert.pem。如果只填 cert.pem,浏览器会因为缺少中间证书链而报错 ERR_CERT_INCOMPLETE_CHAIN,桌面上看是“不是完全安全”。这个问题很多新人会遇到,其实只是路径填错了。
配好之后:
bash复制sudo nginx -t
sudo systemctl reload nginx
然后用 curl -vI https://example.com 查看证书链是否完整。输出里能看到 Server certificate 段落,包含 subject 和 issuer,说明证书链正常。
7.5 续期失败怎么办
如果 certbot renew --dry-run 报错,先把完整错误信息记下来,常见的有三种:
Timeout during connect:服务器无法访问 Let’s Encrypt 服务,检查出站 TCP 80/443 是否被防火墙拦截。Invalid response:Webroot 验证路径访问不通,回到 7.2 排查。The currently selected challenge is not supported by any available plugin:说明你申请时用的方式和现有插件不匹配,比如用 DNS Challenge 申请,但没有安装对应 DNS 插件。Cert not yet due for renewal不是错误,它只是告诉你证书还没到续期窗口。
8. 一些实操心得与扩展建议
8.1 把证书申请当“一次性配置”,把续期当“日常自动化”
我最开始接触 Certbot 时,也只关注“怎么把证书申请下来”,完全忽略了续期链路。后来一张证书到期导致线上站点变成不受信任的连接,才意识到可持续性比一次成功更重要。现在我的流程是:证书申请成功后,立刻跑一次 certbot renew --dry-run,并且在一个月后、两个月后各检查一次,确保定时任务正常运行。
8.2 从单一证书扩展到多域名与多站点的建议
如果一台服务器上有多个站点,建议给每个站点分别申请证书,而不是把所有域名塞进一张证书里。理由有三点:一是证书文件分开后,某个域名续期失败不会影响其他域名;二是配置解耦,新增站点时不需要动已有证书;三是可以用不同的验证方式,比如这个站用 Webroot,那个站用 DNS Challenge,互不干扰。
但这里要注意,一张证书里多个域名时,Let‘s Encrypt 的速率限制是“每周最多 50 张证书”,不是“每周最多 50 个域名”。所以如果你有 20 个域名要签,尽量分多张证书申请,避免触发速率限制。
8.3 对国内 DNS 服务商自动续期的补充
国内主流的阿里云、腾讯云可能没有官方 Certbot DNS 插件,但社区方案也不少。我试过比较靠谱的思路是:先用 API 获取 TXT 记录值,然后用 certbot --manual --preferred-challenges dns 配合自定义 hook 脚本,把记录自动写入 DNS。这类插件依赖 Python 3,安装前要确认和当前 certbot 版本兼容。
如果你不想折腾自动 DNS 插件,还有一个很实用的折中方案:用 Webroot 模式申请单域名证书,配合云平台提供的定时任务或 GitHub Actions,定期检查证书剩余天数,并通过云平台的 API 触发一次证书更新。原理一样,只是不用 Certbot 做 DNS 验证而已。
8.4 无论用哪种方式,证书备份都不能省
证书文件虽然只有几个小文本文件,但丢了就得重新申请,而且私钥丢了意味着你必须立刻吊销重建。我一般会把 /etc/letsencrypt/live/ 目录打包加密,备份到对象存储或另一台机器里:
bash复制sudo tar czf letsencrypt-backup.tar.gz /etc/letsencrypt/live /etc/letsencrypt/archive /etc/letsencrypt/renewal
注意只备份这三个目录就够了,accounts 目录是 ACME 账户信息,丢了也不影响证书使用,但会影响续期时和 CA 的账户绑定,所以建议完整存档。
8.5 关于 Let’s Encrypt 证书和浏览器信任
在 Ubuntu 22.04 上用 Certbot 申请下来的 Let‘s Encrypt 证书,到 2025 年之后会切换为 ISRG Root X1 交叉签名的链。绝大多数现代操作系统和浏览器都已经信任 ISRG Root X1。但如果你要部署到一些老旧的嵌入式设备、旧版 Android 系统上,可能会遇到信任链问题。这时候可以启用 Certbot 的 --preferred-chain 参数,指定使用旧的交叉签名链来兼容旧设备。具体的兼容策略因设备而定,但至少要知道这个参数存在,不要等到现场出了问题才抓瞎。
写在最后
Certbot 的三种证书申请方式并不存在谁比谁更好的问题,它们分别对应不同的服务器状态和网络环境。我个人的经验是:只要服务器上跑着 Web 服务,就优先用 Webroot,简单且不打断线上服务;如果服务器本身没有对外开放 80 端口,或者要签泛域名,那就走 DNS Challenge,配合服务商的 API 插件做到全自动;只有临时测试或者服务器上真的什么都没有时,才用 Standalone。在 Ubuntu 22.04 上,只要把环境准备好、把续期链路跑通、把 Nginx 的证书路径写对,后面基本可以做到“一次配置,长期省心”。如果你在操作中遇到这篇文章里没提到的报错,最有效的排查方法是把完整的错误输出抄下来,再对照证书申请、验证路径、续期日志这几个环节逐一排除,绝大多数问题都能在 10 分钟内找到答案。
