Skip to content

Certbot 证书管理

Certbot 是 Let's Encrypt 官方的 ACME 客户端,用于自动化申请和续期免费 HTTPS/TLS 证书。一行命令搞定从申请到部署的完整流程。

速查卡片

项目内容
安装apt install certbot / snap install certbot / brew install certbot
申请+安装certbot --nginx -d example.com
只申请不安装certbot certonly --webroot -w /var/www/html -d example.com
通配符证书certbot certonly --dns-cloudflare -d *.example.com
批量续期certbot renew(systemd timer 自动执行)
测试续期certbot renew --dry-run
查看证书certbot certificates
吊销证书certbot revoke --cert-name example.com
证书路径/etc/letsencrypt/live/<域名>/
配置文件/etc/letsencrypt/cli.ini
续期 Hook--deploy-hook "systemctl reload nginx"
速率限制50 证书/注册域/周、5 重复证书/周、5 次验证失败/小时
测试环境--test-cert(staging,不计入速率限制)

概述

Certbot 是什么

Certbot 是 EFF(电子前哨基金会)维护的 Let's Encrypt 官方命令行客户端。它实现了 ACME(Automated Certificate Management Environment)协议,能够:

  • 自动向 Let's Encrypt CA 证明你对域名的控制权
  • 获取浏览器信任的 HTTPS 证书
  • 自动安装证书到 Web 服务器(Nginx / Apache)
  • 跟踪证书到期时间并自动续期
  • 吊销不再需要的证书

Let's Encrypt 是由 ISRG 运营的免费、自动化、开放的证书颁发机构,签发的证书被所有主流浏览器信任。证书有效期 90 天,需要定期续期。

ACME 验证原理

申请证书前,Let's Encrypt 需要验证你确实控制着该域名。验证通过两种"挑战"(Challenge)完成:

挑战类型原理端口支持通配符
HTTP-01/.well-known/acme-challenge/ 下放置验证文件80
DNS-01在 DNS 中添加 _acme-challenge TXT 记录53

Certbot 通过各种插件自动完成这些挑战,无需手动操作。

安装

Ubuntu / Debian

bash
# 推荐:通过 snap 安装(官方推荐方式)
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

# 或通过 apt(版本可能较旧)
sudo apt update
sudo apt install certbot python3-certbot-nginx

CentOS / RHEL / Fedora

bash
# RHEL 8+ / CentOS Stream / Fedora
sudo dnf install certbot python3-certbot-nginx

# RHEL 7 / CentOS 7
sudo yum install epel-release
sudo yum install certbot python3-certbot-nginx

macOS

bash
brew install certbot

Docker

bash
docker run -it --rm \
  -v /etc/letsencrypt:/etc/letsencrypt \
  -v /var/lib/letsencrypt:/var/lib/letsencrypt \
  -p 80:80 \
  certbot/certbot certonly --standalone -d example.com

当前版本:5.7.0(2026 年 7 月)。可通过 certbot --version 查看本地版本。

核心子命令

Certbot 通过子命令(subcommand)区分操作类型:

run — 申请并安装证书

bash
# 自动检测 Web 服务器并申请+安装
certbot

# 指定 Nginx,申请证书并自动修改 nginx 配置
certbot --nginx -d example.com -d www.example.com

# 指定 Apache
certbot --apache -d example.com

run 是默认子命令,可以省略。这是最常用的命令——一步完成域名验证、证书申请和服务器配置。

certonly — 只申请证书不安装

bash
# webroot 方式(不中断 Web 服务器)
certbot certonly --webroot -w /var/www/html -d example.com

# standalone 方式(临时启动内置 Web 服务器)
certbot certonly --standalone -d example.com

# DNS 方式(通配符证书必须用这个)
certbot certonly --dns-cloudflare -d *.example.com -d example.com

适用场景:证书文件需要手动配置到 Web 服务器、或 Web 服务器不支持自动安装。

renew — 批量续期

bash
# 续期所有即将过期的证书
certbot renew

# 模拟续期(不实际续期,测试流程是否正常)
certbot renew --dry-run

# 强制续期(不管是否快到期)
certbot renew --force-renewal

# 静默续期(适合 cron)
certbot renew --quiet

renew 只会续期距到期 30 天内的证书。大多数发行版安装 Certbot 后会自动配置 systemd timer 或 cron job。--dry-run 使用 staging 环境测试,不计入速率限制

管理类子命令

bash
# 查看所有证书及其到期时间
certbot certificates

# 吊销证书
certbot revoke --cert-name example.com

# 删除证书(先吊销再删除更安全)
certbot delete --cert-name example.com

# 为已有证书追加安全增强(如 HSTS、OCSP Stapling)
certbot enhance --nginx -d example.com

# 修改证书的续期配置(v2.3.0+)
certbot reconfigure --cert-name example.com

验证方式与插件

Certbot 通过"插件"来执行 ACME 挑战。插件分为两种角色:Authenticator(验证域名所有权)和 Installer(安装证书到服务器)。

插件速查

插件验证安装验证方式说明
nginxHTTP-01 (80)自动修改 Nginx 配置
apacheHTTP-01 (80)自动修改 Apache 配置
webrootHTTP-01 (80)在网站根目录放验证文件
standaloneHTTP-01 (80)临时启动内置 Web 服务器
DNS 插件DNS-01 (53)通过 DNS API 自动添加记录
manualHTTP-01 / DNS-01手动完成验证步骤

Nginx 插件

bash
# 基本用法
certbot --nginx -d example.com -d www.example.com

⚠️ Nginx 插件会自动修改 Nginx 配置文件,具体操作包括:

  • 在对应 server 块中添加 listen 443 ssl 指令
  • 写入 ssl_certificatessl_certificate_key 指向证书文件
  • include /etc/letsencrypt/options-ssl-nginx.conf(SSL 安全参数)
  • 如果使用 --redirect,自动添加 HTTP → HTTPS 重定向

防范措施

bash
# 建议操作前先备份
cp -r /etc/nginx /etc/nginx.bak

# 如果配置被改乱了,可以用 rollback 回滚
certbot --nginx rollback

非标准 Nginx 路径可能识别失败,此时可以在 cli.ini 中指定:

ini
nginx-server-root = /usr/local/nginx/conf
nginx-ctl = /usr/local/nginx/sbin/nginx

Webroot 插件

适合已有 Web 服务器运行中、不想中断服务的场景:

bash
# 单域名
certbot certonly --webroot -w /var/www/html -d example.com

# 多域名,每个域名可能在不同目录
certbot certonly --webroot \
  -w /var/www/example -d example.com -d www.example.com \
  -w /var/www/other -d other.example.com

原理:Certbot 在 {webroot-path}/.well-known/acme-challenge/ 下放置一个临时验证文件,Let's Encrypt 验证服务器通过 HTTP 请求该文件来确认域名控制权。

确保 Web 服务器允许访问 /.well-known/ 隐藏目录。Nginx 默认允许,Apache 可能需要额外配置。

Standalone 插件

适合没有 Web 服务器运行的情况,Certbot 会临时启动一个内置 Web 服务器:

bash
# 需要先停止占用 80 端口的服务
systemctl stop nginx
certbot certonly --standalone -d example.com
systemctl start nginx

必须绑定 80 端口,所以需要先停掉占用该端口的服务。如果 80 端口被占用,也可以指定其他端口但需要防火墙配合。

DNS 插件

DNS 插件通过 API 自动修改 DNS 记录来完成 DNS-01 挑战,是获取通配符证书的唯一方式

以 Cloudflare 为例:

bash
# 1. 安装 DNS 插件
sudo apt install python3-certbot-dns-cloudflare

# 2. 创建 API token 配置文件(权限:Zone:DNS:Edit)
cat > ~/.secrets/cloudflare.ini << EOF
dns_cloudflare_api_token = 你的Cloudflare_API_Token
EOF
chmod 600 ~/.secrets/cloudflare.ini

# 3. 申请通配符证书
certbot certonly --dns-cloudflare \
  --dns-cloudflare-credentials ~/.secrets/cloudflare.ini \
  -d *.example.com -d example.com

支持的主流 DNS 插件:certbot-dns-cloudflarecertbot-dns-route53(AWS)、certbot-dns-digitaloceancertbot-dns-googlecertbot-dns-rfc2136 等。

组合使用插件

可以将验证和安装分离,使用不同的插件:

bash
# webroot 验证 + nginx 安装
certbot run -a webroot -i nginx -w /var/www/html -d example.com

# DNS 验证 + apache 安装
certbot run -a dns-cloudflare -i apache -d example.com

Manual 手动模式

bash
certbot certonly --manual -d example.com

Certbot 会给出提示,要求你手动完成验证(放置文件或添加 DNS 记录)。⚠️ 手动模式创建的证书不支持自动续期,除非配合 --manual-auth-hook 使用自动化脚本。

证书管理

证书文件路径

申请成功后,证书存放在 /etc/letsencrypt/live/<证书名>/

/etc/letsencrypt/live/example.com/
├── cert.pem          ← 证书(不含中间证书)
├── chain.pem         ← 中间证书
├── fullchain.pem     ← 完整证书链(cert.pem + chain.pem) ← Nginx 用这个
├── privkey.pem       ← 私钥
└── README            ← 说明文件(实际是符号链接)

这些文件实际是指向 /etc/letsencrypt/archive/ 的符号链接。不要移动或删除 archive 下的源文件

Nginx 配置示例

nginx
server {
    listen 443 ssl http2;
    server_name example.com www.example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # SSL 安全参数(Certbot 自动生成的推荐配置)
    include /etc/letsencrypt/options-ssl-nginx.conf;

    # OCSP Stapling
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
}

查看证书信息

bash
# 查看所有 Certbot 管理的证书及到期时间
certbot certificates

# 查看单个证书详情
openssl x509 -in /etc/letsencrypt/live/example.com/fullchain.pem -text -noout

修改证书域名

bash
# 为已有证书追加域名(会重新申请)
certbot --nginx --cert-name example.com -d example.com -d www.example.com -d api.example.com

# 如果希望自动扩展,使用 --expand
certbot --nginx --expand -d example.com -d www.example.com -d api.example.com

RSA 与 ECDSA 密钥

参数说明
--key-type rsa指定 RSA 密钥
--key-type ecdsa指定 ECDSA 密钥(v5.x 起默认值
--rsa-key-size 4096RSA 密钥长度(默认 2048)
--elliptic-curve secp256r1ECDSA 曲线(默认 secp256r1)
bash
# 申请 RSA 4096 位证书
certbot --nginx --key-type rsa --rsa-key-size 4096 -d example.com

# ECDSA(默认,无需指定)
certbot --nginx -d example.com

ECDSA 密钥更小、更快、更安全,现在已是 Certbot 默认选项。如果目标客户端较老(Android 4.x 等),用 RSA 兼容性更好。

自动续期

systemd timer(推荐)

大多数通过系统包管理器安装的 Certbot 会自动创建 systemd timer:

bash
# 检查 timer 状态
systemctl status certbot.timer
systemctl list-timers | grep certbot

# 手动触发
systemctl start certbot.service

# timer 配置文件位置
systemctl cat certbot.timer

每天运行两次(随机时间),只续期 30 天内到期的证书。

cron 手动配置

bash
# 编辑 root crontab
sudo crontab -e

# 每天凌晨 3:15 和下午 3:15 各执行一次
15 3,15 * * * certbot renew --quiet --post-hook "systemctl reload nginx"

使用随机分钟数(0-59),避免所有 Let's Encrypt 客户端同时请求。

测试续期流程

bash
# 不实际续期,验证流程是否正常(强烈推荐先跑一遍)
certbot renew --dry-run

--dry-run 使用 staging 环境,不计入生产速率限制,如果成功输出 Congratulations, all simulated renewals succeeded 就说明续期配置没问题。

续期 Hook 体系

Certbot 提供了三层 Hook,精确控制续期前后的行为:

Hook触发时机适用场景
--pre-hook续期前停止 Web 服务器(standalone 模式需要)
--post-hook续期后(无论成败)重启 Web 服务器
--deploy-hook每次证书成功续期后重载 Nginx、复制证书到其他位置
bash
# standalone 模式续期:先停 Nginx,续完再启动
certbot renew --pre-hook "systemctl stop nginx" --post-hook "systemctl start nginx"

# 续期成功后重载 Nginx(推荐)
# 方式一:命令行参数
certbot renew --deploy-hook "systemctl reload nginx"

# 方式二:deploy 钩子目录(可放多个脚本)
# 在 /etc/letsencrypt/renewal-hooks/deploy/ 下放可执行脚本
cat > /etc/letsencrypt/renewal-hooks/deploy/nginx-reload.sh << 'EOF'
#!/bin/bash
systemctl reload nginx
EOF
chmod +x /etc/letsencrypt/renewal-hooks/deploy/nginx-reload.sh

--deploy-hook 只在证书确实续期成功时执行。--post-hook 不管成败都执行。

续期配置文件

每个证书的续期配置独立存放在 /etc/letsencrypt/renewal/<证书名>.conf

ini
# /etc/letsencrypt/renewal/example.com.conf
version = 2.8.1
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

# 续期参数
authenticator = nginx
installer = nginx
key_type = ecdsa

# 自定义 hook
deploy_hook = systemctl reload nginx

修改后可通过 certbot reconfigure --cert-name example.com 或直接编辑该文件。

通配符证书

Let's Encrypt 支持通配符证书(*.example.com),但只能通过 DNS-01 验证

完整流程(Cloudflare)

bash
# 1. 安装插件
sudo apt install python3-certbot-dns-cloudflare

# 2. 配置 API token
# Cloudflare 控制台 → 个人资料 → API 令牌 → 创建令牌
# 权限:Zone - DNS - Edit
# 区域资源:指定域名

cat > ~/.secrets/cloudflare.ini << EOF
dns_cloudflare_api_token = 你的API_Token
EOF
chmod 600 ~/.secrets/cloudflare.ini

# 3. 申请通配符 + 根域名
certbot certonly --dns-cloudflare \
  --dns-cloudflare-credentials ~/.secrets/cloudflare.ini \
  -d example.com -d *.example.com

# 4. 配置 Nginx 使用同一个证书
ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

通配符续期注意事项

bash
# DNS 插件的证书自动续期没问题(systemd timer 自动执行)
# 但如果 API token 过期,续期会失败——确保 token 长期有效

# 可以给续期添加通知 hook
certbot renew --deploy-hook "curl -X POST https://你的告警Webhook"

Let's Encrypt 速率限制

Let's Encrypt 对证书申请有严格的速率限制,用令牌桶算法实现。开发和测试时务必使用 --test-cert 指向 staging 环境。

核心限制一览

限制项额度时间窗口说明
每个注册域名的证书数507 天每 202 分钟恢复 1 个额度
相同域名组合的重复证书57 天每 34 小时恢复 1 个额度
验证失败次数51 小时每个账户每个域名单独计算
每个账户的订单数3003 小时每 36 秒恢复 1 个
每个 IP 注册账户数103 小时

最容易踩的坑

1. 重复证书限制(5次/7天)

最常见的原因:测试时反复运行 certbot --nginx -d example.com,每次都会申请完全相同域名组合的新证书。

bash
# ❌ 反复申请同一个域名组合
certbot --nginx -d example.com  # 第1次 OK
certbot --nginx -d example.com  # 第2次 OK
certbot --nginx -d example.com  # ...
# ...第6次 → 触发限制!

# ✅ 测试时使用 staging 环境
certbot --nginx --test-cert -d example.com

# ✅ 已申请过就用 renew,不会重复申请
certbot renew

2. 验证失败限制(5次/小时)

bash
# 每次失败的申请都消耗额度
# 比如端口 80 没开放,反复尝试 5 次后会被锁 1 小时

# ✅ 先用 --dry-run 验证流程
certbot certonly --dry-run --webroot -w /var/www/html -d example.com

# ✅ 排查问题期间用 staging
certbot --test-cert --nginx -d example.com

3. 续期不受重复证书限制

续期(renew)操作不会触发"重复证书"限制,可以放心配置自动续期。

规避策略

  • 开发/测试:一律使用 --test-cert(staging 环境,限制大幅放宽)
  • 多子域名:尽量合并到一个证书(SAN),而不是分别申请
  • 先 dry-run:正式申请前先用 --dry-run 跑一遍
  • 别频繁删除重建:删除再申请 = 新证书,不是续期

常见排错

Nginx 配置被改乱了

Certbot 的 nginx 插件会自动修改 /etc/nginx/ 下的配置文件。如果出现问题:

bash
# 方法一:rollback 回滚
certbot --nginx rollback

# 方法二:手动还原备份
cp /etc/nginx.bak/nginx.conf /etc/nginx/nginx.conf
nginx -t && systemctl reload nginx

如果之前没有备份,检查 /etc/letsencrypt/ 不会影响 nginx 原始配置——Certbot 修改的是 nginx 的 server 块内容,证书文件路径是独立管理的。

端口 80 不可达

Error: Could not bind to IPv4 or IPv6 port 80

原因:端口 80 被占用、防火墙拦截、或 ISP 屏蔽。

bash
# 检查端口占用
ss -tlnp | grep :80

# 解决方式一:用 webroot(不需要绑定端口)
certbot certonly --webroot -w /var/www/html -d example.com

# 解决方式二:用 DNS 验证
certbot certonly --dns-cloudflare -d example.com

# 解决方式三:停掉占用 80 端口的服务后,standalone
systemctl stop nginx
certbot certonly --standalone -d example.com
systemctl start nginx

速率限制触发

too many certificates already issued for exact set of domains
bash
# 先看当前有哪些证书
certbot certificates

# 等限制恢复(无法加速),或者:
# 1. 给域名组合加个子域名(改变 "exact set" 即可绕过)
certbot --nginx -d example.com -d www.example.com -d temp.example.com

# 2. 用 staging 环境继续测试
certbot --nginx --test-cert -d example.com

DNS 验证超时

DNS problem: looking up TXT for _acme-challenge.example.com

DNS 记录传播需要时间。DNS 插件的厂商 API 通常会等待传播完成,但如果用 --manual 手动添加 TXT 记录:

bash
# 添加记录后等 5-20 分钟后按 Enter 继续
# 可以先用 dig 确认记录已生效:
dig _acme-challenge.example.com TXT

续期失败但不影响服务

Attempting to renew cert ... produced an unexpected error

续期失败时,已有证书在到期前仍然有效。有充足时间排查:

bash
# 查看哪个证书续期失败
certbot renew --dry-run

# 单独续期该证书看详细错误
certbot renew --cert-name example.com --force-renewal -v

# 常见原因:
# - Nginx 配置有问题 → nginx -t 检查
# - 域名 DNS 解析变了
# - 验证路径不可达(检查防火墙/安全组 80/443 端口)

Apache/Nginx 插件未检测到

bash
# certbot 找不到你的 Web 服务器
# 指定正确的服务器根路径:
certbot --apache --apache-server-root /usr/local/apache2
certbot --nginx --nginx-server-root /usr/local/nginx/conf

最佳实践

密钥类型选择

场景推荐原因
现代 Web 应用(2020+)ECDSA(默认)更快、更小、更安全
需要兼容老设备RSA 2048Android 4.x / IE11 等
高安全要求RSA 4096合规要求

DNS API 凭证安全

bash
# ✅ 用范围最小的 API token,不是全局 API key
# Cloudflare:Zone:DNS:Edit 权限,只限于特定域名
# Route53:IAM 策略限制到特定 hosted zone

# ✅ 配置文件权限
chmod 600 ~/.secrets/cloudflare.ini

# ❌ 不要把凭证放 /etc/letsencrypt/cli.ini(全局可读!)

通配符 vs 多域名 SAN

通配符多域名 SAN
示例*.example.coma.example.com, b.example.com
验证方式必须 DNS-01HTTP-01 即可
覆盖范围所有子域名(仅一级)精确指定的域名
安全影响私钥泄露影响所有子域名影响范围可控
推荐子域名多且动态变化子域名固定且数量少

证书到期监控

bash
# 方式一:cron 定期检查
# 每天凌晨检查,30 天内到期发告警
0 0 * * * certbot certificates 2>/dev/null | grep -q "VALID: [0-9]\{1,2\} days" && \
  echo "Certificate expiring soon!" | mail -s "SSL Alert" admin@example.com

全局配置文件

/etc/letsencrypt/cli.ini 中设置默认参数,避免每次手动指定:

ini
# 默认同意协议
agree-tos = true

# 通知邮箱
email = admin@example.com

# RSA 密钥长度
rsa-key-size = 2048

# 默认使用 webroot 验证
authenticator = webroot

# HTTP 端口(非标端口)
http-01-port = 8080

来源

来源链接
Certbot 官方文档https://eff-certbot.readthedocs.io/en/stable/
Let's Encrypt 速率限制https://letsencrypt.org/docs/rate-limits/
ACME 挑战类型https://letsencrypt.org/docs/challenge-types/
Certbot 源码https://github.com/certbot/certbot
版本5.7.0(整理时最新稳定版)