1. 背景与适用范围

本文介绍使用 Let's Encrypt、Nginx 与 Certbot 自动续约 HTTPS 证书的通用方案,适用于 Ubuntu 等使用 systemd 的 Linux 发行版,不依赖 Docker。

文中的 example.comwww.example.comnew.example.com 均为示例域名,请替换为实际域名。

2. 架构

flowchart LR
  U["浏览器"] -->|"HTTPS 443"| N["Nginx"]
  N -->|"读取证书与私钥"| C["/etc/letsencrypt/live/example.com/"]
  T["systemd certbot.timer"] -->|"定期执行 certbot renew"| B["Certbot"]
  B -->|"ACME HTTP-01 校验"| L["Let's Encrypt"]
  B -->|"更新证书文件"| C
  B -->|"续约成功后执行"| H["deploy hook"]
  H -->|"验证配置并重载"| N

职责说明:

  • Nginx:处理 HTTP/HTTPS 请求,并通过证书和私钥提供 TLS。
  • Certbot:向 Let's Encrypt 申请、续约和部署证书。
  • Let's Encrypt:签发证书的 ACME 证书颁发机构。
  • systemd timer:定期运行 certbot renew,仅在证书进入续约窗口时真正续约。

3. 示例配置

Nginx 的 HTTPS server block 应引用 Certbot 管理的证书文件:

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

这些文件由 Certbot 管理。证书续约后,Nginx 重载才会在新建 TLS 连接中使用新证书。

Certbot 的续约配置通常位于:

/etc/letsencrypt/renewal/example.com.conf

使用 Nginx 插件时,续约配置中通常包含:

authenticator = nginx
installer = nginx
server = https://acme-v02.api.letsencrypt.org/directory

4. 自动续约流程

certbot.timer 会定期执行 certbot renew。默认情况下,Certbot 会在证书距离到期日不足 30 天时尝试续约。

续约成功后,通常会经历以下步骤:

  1. Certbot 向 Let's Encrypt 发起续约请求。
  2. Certbot 使用 Nginx 插件处理 /.well-known/acme-challenge/ 的 HTTP-01 校验。
  3. Let's Encrypt 通过公网 80 端口验证域名。
  4. 新证书写入 /etc/letsencrypt/archive/example.com/
  5. /etc/letsencrypt/live/example.com/ 中的链接指向最新证书。
  6. Certbot 通过已配置的 installer 或 deploy hook 部署新证书,并重载 Nginx。

注意:HTTP-01 校验要求 Let's Encrypt 能通过公网访问 80 端口。即使网站会将 HTTP 重定向至 HTTPS,验证期间也必须允许挑战请求通过。

4.1 确保续约后重载 Nginx

建议创建显式的 deploy hook,以确保只有在续约成功且 Nginx 配置检查通过后才重载服务:

# 创建续约成功后执行的 Nginx 重载钩子
sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh >/dev/null <<'EOF'
#!/bin/sh
# 仅在 Nginx 配置检查通过时重载服务
nginx -t && systemctl reload nginx
EOF

# 允许 Certbot 执行部署钩子
sudo chmod 755 /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

使用测试环境验证续约和部署钩子:

# 演练续约流程,并执行 deploy hook
certbot renew --dry-run --run-deploy-hooks

5. 日常检查

5.1 查看证书状态

# 列出 Certbot 管理的证书及到期时间
certbot certificates

5.2 查看定时器状态和下次执行时间

# 查看定时器是否已启用
systemctl status certbot.timer

# 查看 Certbot 相关定时任务的下次执行时间
systemctl list-timers --all | grep certbot

5.3 查看最近续约日志

# 查看最近的续约、成功和错误日志
grep -Ei 'renew|success|error|example\.com' /var/log/letsencrypt/letsencrypt.log | tail -n 100

5.4 验证 Nginx 使用的证书

# 查询远端站点返回的证书签发者、主题和有效期
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -issuer -subject -dates

5.5 检查 Nginx 配置

# 检查配置语法并查看服务状态
nginx -t
systemctl status nginx

6. 安全的续约演练

使用 Let's Encrypt 测试环境验证续约流程,不会消耗正式签发额度:

# 使用测试环境模拟续约
certbot renew --dry-run

演练成功后无需重启服务器。若配置了 deploy hook,可使用前文的 --run-deploy-hooks 参数同时验证重载流程。

7. 手动续约

通常无需手动续约。仅在排障或证书接近到期且定时任务异常时使用:

# 对进入续约窗口的证书执行续约
certbot renew

仅对单个证书强制重新申请会受到 Let's Encrypt 频率限制,应避免在日常操作中使用:

# 强制续约指定证书,按实际证书名称替换 example.com
certbot renew --cert-name example.com --force-renewal

执行前后均应检查:

# 确认证书状态与 Nginx 配置均正常
nginx -t
certbot certificates

8. 为新服务器首次签发证书

前提条件:

  1. 域名 A 或 AAAA 记录已解析到服务器公网 IP。
  2. 云防火墙和本机防火墙允许公网访问 TCP 80、443。
  3. Nginx 已安装,并存在包含目标 server_name 的 HTTP server block。
  4. 80 端口未被其他服务占用。

安装组件:

# 更新软件包索引并安装 Nginx、Certbot 及其 Nginx 插件
sudo apt-get update
sudo apt-get install -y nginx certbot python3-certbot-nginx

先验证 Nginx:

# 检查配置并设置 Nginx 开机启动
sudo nginx -t
sudo systemctl enable --now nginx

签发并自动写入 Nginx TLS 配置:

# 为示例主域名和 www 子域名签发证书
sudo certbot --nginx -d example.com -d www.example.com

完成后验证:

# 检查证书、Nginx 配置、定时器和续约演练
sudo certbot certificates
sudo nginx -t
sudo systemctl status certbot.timer
sudo certbot renew --dry-run

9. 域名增加或变更

新增域名前,先完成 DNS、80/443 防火墙和 Nginx server_name 配置,再执行:

# 重载更新后的 Nginx 配置
sudo nginx -t && sudo systemctl reload nginx

# 为更新后的域名集合重新申请或扩展证书
sudo certbot --nginx --cert-name example.com -d example.com -d www.example.com -d new.example.com

域名不再使用时,不要直接删除 /etc/letsencrypt/live/ 下的文件;应先从 Nginx 配置移除对应引用并验证 Nginx,再通过 Certbot 管理证书生命周期。

10. 常见故障排查

10.1 HTTP-01 校验失败

检查 DNS 是否已指向本机:

# 查询示例域名的解析结果
getent ahosts example.com

检查 80 端口监听:

# 查看 TCP 80 端口的监听进程
ss -ltnp '( sport = :80 )'

检查 Nginx 和云防火墙是否允许 /.well-known/acme-challenge/ 请求。不要只开放 443;HTTP-01 校验需要公网访问 80。

10.2 Nginx 配置检查失败

# 查看 Nginx 配置错误和最近的服务日志
nginx -t
journalctl -u nginx -n 100 --no-pager

先修复 Nginx 配置,再重新运行 certbot renew --dry-run

10.3 定时器未运行

# 启用 Certbot 定时器并查看服务日志
sudo systemctl enable --now certbot.timer
sudo systemctl status certbot.timer
sudo journalctl -u certbot.service -n 100 --no-pager

部分发行版也会安装 /etc/cron.d/certbot 作为无 systemd 环境下的备用调度方式。应以实际启用的调度方式为准,避免重复执行。

11. 重要文件与权限

路径 用途
/etc/letsencrypt/live/example.com/fullchain.pem Nginx 使用的服务器证书与中间证书链
/etc/letsencrypt/live/example.com/privkey.pem Nginx 使用的私钥,必须严格保护
/etc/letsencrypt/archive/example.com/ Certbot 保存的历史证书版本
/etc/letsencrypt/renewal/example.com.conf 续约参数
/var/log/letsencrypt/letsencrypt.log Certbot 日志

不要将 privkey.pem 复制到代码仓库、应用目录、聊天记录或不受控的备份介质中。不要手动修改 live 目录内的链接或删除 archive 中的文件。

12. 验证与总结

完成首次签发或续约演练后,应确认 nginx -t 通过、证书有效期符合预期,且 Certbot 的定时任务已启用。日常保持 80 端口可用于 HTTP-01 验证,并定期查看续约日志即可。