certbot — Let's Encrypt 证书自动化管理详解
certbot 是 EFF(电子前沿基金会)出品的 ACME 协议客户端,用来从 Let's Encrypt(及其它兼容 ACME 的 CA)免费获取、自动续期并部署 HTTPS/TLS 证书。它是当前互联网上部署 HTTPS 最主流的自动化工具,也是"加密整个互联网"(encrypt the entire Internet)运动的核心组件。
本页先梳理 certbot 的"是什么"与行为/结构,再给出实战操作手册与参数指令速查。
1. 概述
1.1 是什么
certbot 是一个运行在服务器命令行的 Python 工具(certbot 命令)。它帮你完成两件事:
- 获取证书(Obtain) — 自动完成 ACME 协议要求的"证明你拥有该域名"的验证步骤,把证书保存到
/etc/letsencrypt/live/,并按计划自动续期。 - 安装证书(Install) — 可选地把证书部署到 Apache / nginx 等 web 服务器,自动修改服务器配置开启 HTTPS。
它不是 CA(证书颁发机构)本身,而是连接"你"和"CA"的客户端代理。
1.2 生态:TLS / CA / ACME / Let's Encrypt
要理解 certbot,先分清这几个角色:
- TLS/SSL(传输层安全协议) — 加密浏览器与服务器之间通信的协议,HTTPS = HTTP over TLS。证书用于密钥交换与服务器身份认证。
- 数字证书(Certificate) — 包含公钥、服务器身份信息、签发者签名。浏览器信任签发者(CA)→ 信任证书。
- CA(Certificate Authority,证书颁发机构) — 签发证书的可信第三方。
- ACME(Automatic Certificate Management Environment,自动证书管理环境) — 定义"如何自动申请/验证/续期/吊销证书"的协议标准,即 RFC 8555。certbot 是这个协议的客户端实现。
- Let's Encrypt(让加密) — EFF、Mozilla 等发起的免费开放 CA,通过 ACME 提供 DV 证书。certbot 默认对接的就是它。
- ACME(Automatic Certificate Management Environment) — 自动化证书管理的协议标准(RFC 8555),certbot 即其客户端。
- CA(Certificate Authority,证书颁发机构) — 签发数字证书的可信第三方。
- Let's Encrypt(让加密) — EFF 等发起的免费开放 CA,使用 ACME 协议。
- DV(Domain Validation,域名验证) — 只验证"你控制该域名"的证书类型,Let's Encrypt 只发这种。
- Challenge(验证挑战) — ACME 服务器要求你证明域名控制权的方式,分 http-01 与 dns-01 两类。
- Authenticator(认证器) — 负责完成 Challenge 的插件。
- Installer(安装器) — 负责把证书配置进服务器的插件。
- PEM(Privacy-Enhanced Mail) — 证书/密钥的文本编码格式(base64 + 头尾标识)。
1.3 核心心智模型:Lineage(谱系)
certbot 引入 Lineage(证书谱系) 概念:它是一个证书在历次续期中的所有版本 + 该证书的 certbot 配置的集合。每次续期都沿用同一配置,除非显式修改(如增删域名)。
--cert-name 指向 Lineage,而不是直接指向某个证书文件。
每个 Lineage 在 /etc/letsencrypt/live/<cert-name>/ 下有一组符号链接,指向 /etc/letsencrypt/archive/<cert-name>/ 里最新的实际文件:
# live/ 下的符号链接(你程序里永远引用这里) /etc/letsencrypt/live/example.com/cert.pem → 证书本体 /etc/letsencrypt/live/example.com/chain.pem → 中间证书链 /etc/letsencrypt/live/example.com/fullchain.pem → 证书+中间链合并 /etc/letsencrypt/live/example.com/privkey.pem → 私钥(本地生成,永不上传) # archive/ 下是按续期时间戳归档的真实文件 /etc/letsencrypt/archive/example.com/cert1.pem cert2.pem ... # renewal/ 下是每个 lineage 的续期配置 /etc/letsencrypt/renewal/example.com.conf
代码演示
# 用 openssl 查看签发的证书 sudo openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -noout -text # 查看 fullchain(程序常用这个) sudo cat /etc/letsencrypt/live/example.com/fullchain.pem
1.4 安装方式
| 方式 | 适用 | 特点 |
|---|---|---|
| Snap(官方推荐) | systemd 发行版(Ubuntu/Debian/…) | 自动预配续期定时器、自动更新,x86_64/ARMv7/ARMv8 |
| Docker | 容器化环境 | 不能自动安装到 web 服务器,需挂载卷并手动部署证书 |
| Pip | 虚拟环境 | 官方"尽力而为"支持,建议在 venv 里用 |
| 发行版包 | 第三方维护 | 如 apt install certbot,可能滞后于官方版本 |
2. 行为图(系统"做什么")
2.1 用例图 — 交互全景
管理员通过 certbot 完成签发、续期、吊销、删除、查看、配置等用例;certbot 与 CA(Let's Encrypt)通过 ACME 协议交互,并操作 web 服务器 / DNS。
用例演示
certbot certificates # 查看所有证书 certbot certonly -d example.com # 只签发不安装 certbot --nginx -d example.com # 签发并自动装进 nginx certbot renew --dry-run # 测试性续期 certbot revoke --cert-name example.com # 吊销 certbot delete --cert-name example.com # 删除
certonly 子命令;Run(运行)指"签发+安装"一体模式,是默认子命令。
2.2 活动图 — certonly 签发主流程
以最常用的 certonly + webroot 认证为例,走一遍完整主流程:解析参数 → 生成密钥对 → 创建 ACME 账号 → 发起订单 → 完成验证 → 获取并保存证书 → 记录续期配置。
代码演示(真实命令序列)
# 1. 生成密钥对 + 创建/复用 ACME 账号 certbot certonly --webroot -w /var/www/html -d example.com --email you@example.com --agree-tos # 2. certbot 会临时写入验证文件到 webroot/.well-known/acme-challenge/ # 3. CA 验证通过后,证书落到 live/ 并生成 renewal/ 配置 # 4. 查看结果 certbot certificates
2.3 时序图 — http-01 域名验证
http-01 是最常见的验证方式:certbot 在 webroot 放一个随机内容文件,CA 服务器通过 HTTP 访问 http://域名/.well-known/acme-challenge/<token>,能取到就证明你控制该域名。整个过程走 ACME 协议的 JWS 签名消息。
代码演示(挑战文件长相)
# certbot 在 webroot 下生成的文件(token 目录 + key authorization 内容) # 请求路径 GET /.well-known/acme-challenge/HGr8U1IeTW4kY_Z6UIyaakzOkyQgPr_7ArlLgtZE8SX # 响应内容形如:HGr8U1IeTW4kY_Z6UIyaakzOkyQgPr_7ArlLgtZE8SX.<账号公钥指纹> # 用 curl 模拟 CA 校验: curl http://example.com/.well-known/acme-challenge/HGr8U1IeTW4kY_Z6UIyaakzOkyQgPr_7ArlLgtZE8SX
_acme-challenge TXT 记录里放随机值,唯一能签发通配符证书(*.example.com)的方式;JWS(JSON Web Signature)——ACME 客户端发给服务器的每条消息都经账号私钥签名。
2.4 状态机图 — Lineage 生命周期
一个 Lineage 从签发到删除的完整生命周期,以及续期/过期/吊销等状态迁移。
代码演示
# 查看状态(VALID 表示还有效,距离过期天数会显示) certbot certificates # Found the following certificates: # Certificate Name: example.com # Domains: example.com, www.example.com # Expiry Date: 2026-08-20 12:00:00+00:00 (VALID: 87 days) # Certificate Path: /etc/letsencrypt/live/example.com/fullchain.pem
3. 结构图(系统"由什么组成")
3.1 部署图 — 生态部署
certbot 部署在你的 web 服务器上,通过 ACME 协议与 Let's Encrypt CA 通信;验证时 web 服务器对外提供 HTTP/DNS 响应;签发后的证书供 nginx/apache 等加载。
代码演示(Docker 部署示例)
sudo docker run -it --rm --name certbot \ -v "/etc/letsencrypt:/etc/letsencrypt" \ -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \ -p 80:80 \ certbot/certbot certonly --standalone -d example.com
3.2 组件图 — 内部模块
certbot 源码按职责拆分为多个包:acme(协议层)、certbot(主客户端)、插件包(apache/nginx/dns-*),通过插件接口解耦。
代码演示(列出已安装插件)
certbot plugins # 会列出 certbot.plugins 入口点注册的认证器与安装器
importlib.metadata 把插件注册到 certbot.plugins 组,certbot 启动时动态发现插件,因此第三方可以写插件而不改主程序。
3.3 包图 — /etc/letsencrypt 目录布局
证书、私钥、续期配置、账号、钩子脚本的存放结构。所有文件均为 PEM 编码;私钥只在你本地,从不上传 CA。
代码演示(查看目录树)
sudo tree /etc/letsencrypt
# ├── live/example.com/{cert,chain,fullchain,privkey}.pem # 符号链接
# ├── archive/example.com/{cert1,chain1,fullchain1,privkey1}.pem
# ├── renewal/example.com.conf
# ├── renewal-hooks/{pre,deploy,post}/ # 钩子脚本目录
# ├── accounts/acme-v02.api.letsencrypt.org/directory/<acct>/
# └── cli.ini # 全局配置(可选)
3.4 类图 — 插件架构
certbot 通过两个核心接口扩展能力:Authenticator(完成挑战证明域名控制权)与 Installer(把证书配置进服务器)。内置的 Apache/nginx 插件同时实现两者;standalone 只做认证;DNS 插件只做 dns-01 认证。
代码演示(组合认证器+安装器)
# 用 webroot 做认证、apache 做安装的混合模式 certbot run -a webroot -i apache -w /var/www/html -d example.com # 认证器必填;安装器只在要装进服务器时需要
get_chall_pref()(声明支持的挑战类型优先级)、perform(achalls)(完成挑战)、cleanup(achalls)(验证后清理临时文件/记录);Installer 接口负责修改服务器配置加载证书,并可通过 supported_enhancements() 声明额外安全增强(如 HSTS、HTTP→HTTPS 跳转)。
4. 实战操作手册
4.1 安装
Snap(推荐,Ubuntu/Debian):
sudo apt update && sudo apt install snapd sudo snap install --classic certbot sudo ln -s /snap/bin/certbot /usr/bin/certbot # 放入 PATH
Docker:
docker run -it --rm --name certbot \ -v "/etc/letsencrypt:/etc/letsencrypt" \ -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \ -p 80:80 certbot/certbot certonly --standalone -d example.com
Pip(虚拟环境):
python3 -m venv certbot-venv && source certbot-venv/bin/activate pip install certbot certbot --version
4.2 签发证书(各插件场景)
场景 A:nginx 服务器,自动安装
# 自动检测 nginx 配置、签发并安装、开启自动跳转 HTTPS sudo certbot --nginx -d example.com -d www.example.com --email you@example.com --agree-tos
场景 B:Apache 服务器,自动安装
sudo certbot --apache -d example.com
场景 C:有 web 服务器但不装到服务器(webroot)
# webroot 是网站根目录,不改服务器配置,只临时写验证文件 sudo certbot certonly --webroot -w /var/www/html -d example.com -d www.example.com # 多域名不同 webroot:每个 -w 作用于其后的 -d sudo certbot certonly --webroot -w /var/www/example -d example.com -w /var/www/other -d other.example.net
场景 D:无 web 服务器(standalone,需空闲 80 端口)
sudo certbot certonly --standalone -d example.com # 若需 IPv6/IPv4 指定监听地址 sudo certbot certonly --standalone --http-01-address 0.0.0.0 -d example.com
场景 E:通配符证书(dns-01,唯一方式)
# 以 cloudflare 为例(需先装插件 + API 凭据) pip install certbot-dns-cloudflare sudo certbot certonly --dns-cloudflare \ --dns-cloudflare-credentials /root/.cloudflare.ini \ -d example.com -d '*.example.com' # 插件通过 DNS API 自动加/删 _acme-challenge 的 TXT 记录
场景 F:完全手动(manual,适合无法自动化的环境)
# 交互式提示你手动放置文件或 TXT 记录;不支持自动续期(除非配 auth hook) sudo certbot certonly --manual -d example.com sudo certbot certonly --manual --preferred-challenges dns -d example.com
--manual-auth-hook 脚本自动化验证步骤。能用其他插件就别用 manual。
4.3 证书管理
查看
certbot certificates # 列出所有 lineage certbot certificates --cert-name example.com # 只看某一个
更新域名集合
# 用 --cert-name 精确修改某 lineage 的域名(可增可删) sudo certbot certonly --cert-name example.com -d example.com # 移除 www sudo certbot certonly --cert-name example.com -d example.org -d www.example.org # 整体替换 # 或者用 --expand 只增不减 sudo certbot --expand -d example.com -d newdomain.com
强制更新 / 重复签发
sudo certbot certonly --force-renewal -d example.com # 同域名重新签发,更新 live 软链 sudo certbot certonly --duplicate -d example.com # 生成一个完全独立的证书
切换密钥类型(RSA ↔ ECDSA)
# 默认已是 ECDSA P-256;RSA 2048/4096 可指定 sudo certbot renew --key-type ecdsa --cert-name example.com --force-renewal # 全局默认写进 cli.ini:key-type = ecdsa / rsa-key-size = 4096
吊销
sudo certbot revoke --cert-name example.com sudo certbot revoke --cert-path /etc/letsencrypt/live/example.com/cert.pem sudo certbot revoke --cert-path ... --key-path .../privkey.pem # 用证书私钥吊销 sudo certbot revoke --cert-name example.com --reason keycompromise # 注明原因
删除(不可逆,务必先确认服务器不再引用)
sudo grep -R live/example.com /etc/nginx /etc/httpd /etc/apache2 # 先查引用 sudo certbot delete --cert-name example.com # 再删
重命名
# certbot 无 rename 命令;用 --cert-name 重新签发并删旧的 sudo certbot certonly --cert-name newname -d example.com -d www.example.com sudo certbot install --cert-name newname # 更新服务器配置里的引用 sudo certbot delete --cert-name oldname # 删除旧 lineage
4.4 自动续期与 Hooks
certbot 4.0 起,证书在剩余有效期小于 1/3 时判定"该续期了"(有效期≤10 天的证书阈值放宽到 1/2)。certbot renew 会检查所有 lineage,只续临近过期的,因此可以频繁安全地执行。
检查自动续期是否已配好
sudo certbot renew --dry-run # 干跑一次(用 staging 服务器,不写盘) systemctl list-timers | grep certbot # 看 systemd timer grep certbot /etc/crontab # 或看 cron
手工加 cron(macOS/Linux 通用)
# 每天 0 点和 12 点随机延时后执行,-q 静默
SLEEPTIME=$(awk 'BEGIN{srand(); print int(rand()*(3600+1))}')
echo "0 0,12 * * * root sleep $SLEEPTIME && certbot renew -q" | sudo tee -a /etc/crontab
Hooks(续期前后/部署后执行)
# 命令行 hook:standalone 场景需停/起 web 服务器 sudo certbot renew --pre-hook "service nginx stop" --post-hook "service nginx start" # 只在成功续期后执行部署动作 sudo certbot renew --deploy-hook "/path/to/script" # 目录式 hook(所有子命令都生效,按文件名顺序执行) /etc/letsencrypt/renewal-hooks/pre/ # 续期前 /etc/letsencrypt/renewal-hooks/deploy/ # 续期成功后 /etc/letsencrypt/renewal-hooks/post/ # 续期后 # 示例:重启 haproxy sudo sh -c 'printf "#!/bin/sh\nservice haproxy restart\n" > /etc/letsencrypt/renewal-hooks/post/haproxy.sh' sudo chmod 755 /etc/letsencrypt/renewal-hooks/post/haproxy.sh
RENEWED_DOMAINS 与 FAILED_DOMAINS(空格分隔的域名列表)判断续期结果,决定后续动作。
修改某证书的续期配置(v2.3+ 推荐 reconfigure)
# 会先用 staging 服务器做测试续期,成功才保存 sudo certbot reconfigure --cert-name example.com --webroot-path /path/to/new/location # 老版本流程:dry-run → 带 --force-renewal 正式续期以持久化
5. 参数指令速查手册
5.1 子命令一览
| 分类 | 子命令 | 作用 |
|---|---|---|
| 签发与安装 | run(默认) | 签发并安装到当前 web 服务器 |
certonly | 只签发/续期,不安装 | |
renew | 续期所有临近过期的证书(适合自动化) | |
enhance | 为现有配置添加安全增强(如 HSTS) | |
| 证书管理 | certificates | 显示所有证书信息 |
revoke | 吊销证书(--cert-name 或 --cert-path) | |
delete | 删除证书(--cert-name) | |
reconfigure | 更新证书续期配置(--cert-name) | |
| 账号管理 | register | 创建 ACME 账号 |
unregister | 注销 ACME 账号 | |
update_account | 更新账号信息 | |
show_account | 查看账号详情 | |
| 其他 | plugins / install / help | 列插件 / 安装证书到服务器 / 帮助 |
5.2 常用参数
| 参数 | 说明 |
|---|---|
-d, --domains | 证书包含的域名,可多次指定或用逗号分隔 |
--cert-name | 指定 Lineage 名称(管理操作的关键) |
-a, --authenticator / -i, --installer | 指定认证器 / 安装器插件 |
--apache / --nginx / --webroot / --standalone / --manual | 常用插件的快捷开关 |
-w, --webroot-path | webroot 插件:web 根目录路径 |
--preferred-challenges | 首选挑战类型(http / dns) |
--email / -m | 账号通知邮箱(证书将过期时 CA 会发信) |
--agree-tos | 同意 ACME 订阅协议(非交互必需) |
-n, --non-interactive | 非交互运行(cron 里必用) |
--force-renewal | 忽略到期时间强制续期(勿频繁使用,撞限速) |
--duplicate | 签发一个与现有完全独立的证书 |
--expand | 在现有证书上新增域名 |
--allow-subset-of-names | 部分域名验证成功也继续签发 |
--key-type | rsa / ecdsa(默认 ecdsa) |
--rsa-key-size / --elliptic-curve | RSA 位数(默认 2048)/ ECDSA 曲线(默认 secp256r1) |
--dry-run | 测试性运行(默认用 staging 服务器,不落盘) |
--test-cert / --staging | 用 staging 服务器获取测试证书(无速率限制) |
--server | 指定 ACME 目录 URL(可切其他 CA,如 Sectigo) |
--eab-kid / --eab-hmac-key | 外部账号绑定(EAB)凭据,部分 CA 需要 |
--pre-hook / --post-hook / --deploy-hook | 续期前后 / 成功部署后执行的命令 |
--preferred-challenges | 首选挑战类型 |
-c, --config | 指定配置文件(默认 /etc/letsencrypt/cli.ini) |
--config-dir / --work-dir / --logs-dir | 覆盖默认目录(/etc/letsencrypt / /var/lib/letsencrypt / /var/log/letsencrypt) |
--max-log-backups | 日志保留份数(默认 1000,0 关闭轮转) |
-v / --verbose | 日志详细程度,可 -vvv |
--no-directory-hooks | 不自动运行 renewal-hooks 目录下的脚本 |
--reason | 吊销原因(keycompromise / superseded 等) |
certbot --help all 查看全部参数;certbot --help renew 查看续期相关 hook 细节。
5.3 配置文件 cli.ini / renewal
/etc/letsencrypt/cli.ini 是全局配置,作用于所有调用。默认读取 /etc/letsencrypt/cli.ini 与 ~/.config/letsencrypt/cli.ini。示例:
# cli.ini 示例 key-type = ecdsa elliptic-curve = secp384r1 rsa-key-size = 4096 email = you@example.com # authenticator = standalone # authenticator = webroot # webroot-path = /usr/share/nginx/html # agree-tos = true # server = https://acme-v02.api.letsencrypt.org/directory
renewal/<name>.conf 是证书级续期配置,不建议手工修改,要改请用 certbot reconfigure。
# renewal/example.com.conf 结构(供理解,勿手改) version = 4.2.0 archive_dir = /etc/letsencrypt/archive/example.com cert = /etc/letsencrypt/live/example.com/cert.pem privkey = /etc/letsencrypt/live/example.com/privkey.pem chain = /etc/letsencrypt/live/example.com/chain.pem fullchain = /etc/letsencrypt/live/example.com/fullchain.pem [renewalparams] account = <账号id> server = https://acme-v02.api.letsencrypt.org/directory authenticator = webroot key_type = ecdsa post_hook = "echo 'renewed'"
6. 故障排查与常见问题
| 症状 | 原因 / 排查 |
|---|---|
| http-01 验证失败:Connection refused / 404 | 80 端口未开放或 .well-known 被服务器特殊处理;curl http://域名/.well-known/... 自测 |
| dns-01 验证失败:TXT 记录查不到 | DNS 传播延迟;用 dig _acme-challenge.example.com TXT 检查;确认插件凭据有写权限 |
| too many certificates / too many requests | 撞 Let's Encrypt 速率限制(每周每注册域名 5 张证书);用 --staging 测试、少用 --force-renewal |
| renew 总是失败 / exit code 1 | 看 /var/log/letsencrypt/letsencrypt.log;手动 certbot renew --cert-name X --dry-run -v |
| 证书明明新签仍显示要续期 | --dry-run 不会更新续期记录;真正持久化需一次真实续期 |
| renew 提示 unauthorized(无权限) | 证书是用不同插件/账号签的;重跑原签发命令相同参数 |
| Standalone 端口被占 | 先停 web 服务器或用 webroot 插件;ss -ltnp | grep :80 |
| 客户端不信任 ACME 服务器证书 | 设置 REQUESTS_CA_BUNDLE 环境变量指向你的 CA bundle(certbot 用 requests,不读系统根证书库) |
| 证书未自动续期 | 检查 snap timer / cron 是否存在;certbot renew --dry-run 验证 |
| 多实例并发报锁冲突 | 默认目录有锁文件;并行实例需用不同 --config-dir/--work-dir/--logs-dir |
/var/log/letsencrypt/letsencrypt.log;加 -vvv 能看到 ACME 协议的完整请求/响应。
7. 参考资料
- 官方文档:https://eff-certbot.readthedocs.io
- 源码仓库:https://github.com/certbot/certbot
- 安装指引(按系统生成):https://certbot.eff.org/instructions
- Let's Encrypt:https://letsencrypt.org
- ACME 协议规范:RFC 8555
- 速率限制:https://letsencrypt.org/docs/rate-limits/