Nginx 配置 HTTPS 证书自动续期 (Certbot)

在配置个人博客或网站的 HTTPS 时,Let's Encrypt 配合 Certbot 是最主流的免费方案。理论上它能实现“一次配置,永久自动续期”。但在实际操作中(特别是涉及到服务器迁移、反向代理或从旧配置迁移时),我们经常会遇到 404 UnauthorizedPlugin not installed 等报错。

本文记录了一次真实的排查过程,涵盖了安装插件、解决反向代理验证失败、多域名证书合并以及清理“僵尸”证书的全流程。

1. 为什么自动续期会失败?

Certbot 有两种常用的验证模式:

  1. Webroot 模式 (--webroot):Certbot 在网站根目录创建一个验证文件,Let's Encrypt 访问这个文件来验证。
  2. Nginx 插件模式 (--nginx):Certbot 自动修改 Nginx 配置来完成验证,验证完恢复原状。

常见坑点:如果你的 Nginx 配置了反向代理(例如代理到 Tomcat、Node.js ),Webroot 模式创建的文件会被 Nginx 转发给后端,导致 Let's Encrypt 访问不到文件(报 404 错误)。

解决方案:强烈建议使用 Nginx 插件模式,它能智能处理路由,无需关心反向代理。


2. 基础环境准备

首先,确保你的服务器上不仅安装了 Certbot,还安装了对应的 Nginx 插件。很多续期失败的原因仅仅是缺少这个插件。

Ubuntu / Debian:

sudo apt update
sudo apt install certbot python3-certbot-nginx

CentOS / Alibaba Cloud Linux:

sudo yum install python3-certbot-nginx
# 或者
sudo yum install certbot-nginx

3. 实战:修复 "Unauthorized" 并合并多域名

假设你有两个域名 blog.example.comimg.example.com。之前可能分别申请了证书,且使用的是容易出错的 Webroot 模式。

运行一次“模拟续期”命令。这个命令会跑一遍流程,但不会真的替换证书。

certbot renew --dry-run

当我们尝试续期时,可能会遇到如下报错:

Certbot failed to authenticate some domains ... Invalid response from ... 404

步骤一:强制使用 Nginx 插件重新配置

不需要手动修改 Nginx 配置文件,直接运行以下命令,同时加上你所有的域名:

# -d 后面跟上你所有的域名
sudo certbot --nginx -d blog.example.com -d img.example.com

系统会提示:

You have an existing certificate... Do you want to expand and replace this existing certificate with the new certificate?

输入 E (Expand) 进行扩展合并。

这样做有两个好处:

  1. 强制切换到了更稳定的 Nginx 插件模式。
  2. 将多个域名合并到一张证书里,管理更方便。

4. 清理“僵尸”证书配置

合并证书后,最容易被忽略的一步来了。

当你运行 certbot renew --dry-run 测试时,可能会发现:一个成功了,但还有一个失败了。

The following simulated renewals succeeded:
  /etc/letsencrypt/live/blog.example.com/fullchain.pem (success)

The following simulated renewals failed:
  /etc/letsencrypt/live/img.example.com/fullchain.pem (failure)

原因:因为我们把 img 域名合并到了 blog 的证书里,但系统里还残留着旧的、独立的 img 证书配置文件。这个旧配置还在尝试用老方法续期,自然会报错。

解决方法:

删除旧证书:

# 注意:请根据上一步显示的 Certificate Name 准确填写
sudo certbot delete --cert-name img.example.com

查看当前所有证书:

sudo certbot certificates

你会看到一个是包含所有域名的新证书,一个是多余的旧证书


5. 最终验证 (关键)

不要以为配置完就没事了,一定要进行“模拟续期”测试,确保自动任务能正常工作。

输入命令:

certbot renew --dry-run

如果看到输出中全是 success,没有 failure,恭喜你!你的 HTTPS 证书已经配置为全自动续期,只要服务器不关机,以后都不用操心过期问题了。

检查定时任务是否存在(可选,用于确认),你可以检查一下系统里是否有这个“闹钟”。

systemctl list-timers | grep certbot

如果你看到类似下面的输出,说明定时任务正在运行:

Fri 2020-11-28 20:30:00 CST 9h left n/a n/a certbot.timer certbot.service

原理说明:Certbot 会自动在系统(Cron 或 Systemd)中添加定时任务,每天检查两次。如果证书有效期少于 30 天,它会自动执行续期并重载 Nginx。

总结

在反向代理场景下配置 HTTPS,Nginx 插件模式是最佳选择。

  1. 装插件:确保 python3-certbot-nginx 已安装。
  2. 用插件:使用 certbot --nginx -d 域名 申请或覆盖证书。
  3. 删僵尸:合并证书后,记得 certbot delete 删除旧的冗余配置。
  4. 测续期certbot renew --dry-run 是检验真理的唯一标准。