搬瓦工 HTTPS 证书申请与自动续期教程

搬瓦工 HTTPS 证书申请与自动续期教程
搬瓦工运维与建站教程 · 申请、部署、验证与告警

HTTPS 证书申请成功之后,还要确认 Web 服务加载了它,并且续期流程可以重复完成。很多“自动续期失败”其实是验证路径被改了、DNS 留着旧地址,或者证书文件更新了但 Nginx 仍在使用旧证书。

本文以 Ubuntu / Debian 上的 Nginx 与 Certbot 为例。已经使用面板、acme.sh 或其他证书管理程序时,先确认现有证书归属,不要让多个工具同时改同一组文件。

一、申请前确认域名与入口

证书维护有四个环节
不要只看磁盘文件更新,还要看用户拿到的证书。

确保 A 记录指向正确 IPv4;添加了 AAAA 时,IPv6 的地址、路由、监听和防火墙也要可用。源站换 IP 后应先对照 迁移与地址变更清单,不要继续验证旧节点。

准备 site.example.com 这样的真实域名,不使用示例域名申请。证书中的名称要覆盖实际访问名称,裸域名和 www 是两个不同名称。系统时间、磁盘空间和私钥权限也需要正常。

二、选择长期可自动化的验证方式

HTTP-01 与 DNS-01 怎么选
手动 TXT 验证若没有自动化,不等于可以无人值守续期。

HTTP-01 通过 80 端口的特定路径验证域名;DNS-01 则通过 DNS TXT 记录验证,并可用于通配符。单纯把 Nginx HTTPS 端口改成 8443,不会把 HTTP-01 验证端口一起改掉。

已有网站常用 webroot 方式,不必停止 Nginx。仅有手动 DNS 验证而没有自动更新 TXT 的机制,不能保证定时续期无人值守成功。

三、用 Webroot 签发普通域名证书

确认没有与现有安装方式冲突后安装:

sudo apt update
sudo apt install certbot
sudo install -d -m 0755 /var/www/letsencrypt/.well-known/acme-challenge

在该域名的 80 端口 server 配置中加入验证路径:

location ^~ /.well-known/acme-challenge/ {
    root /var/www/letsencrypt;
    try_files $uri =404;
}

验证文件必须由正确站点返回,不要被全局认证、重写或应用路由接管。配置检查通过后重载,在外部请求一个自己临时建立的测试文件确认路径,然后删除该测试文件。

sudo nginx -t
sudo systemctl reload nginx
sudo certbot certonly --webroot -w /var/www/letsencrypt -d site.example.com
sudo certbot certificates

没有需要 www 时不申请它;需要时先配置 DNS,再追加对应 -d。失败后先读验证错误和日志,不要持续重试以免触及签发限制。[2]

四、Nginx 引用正确证书文件

证书签发后,把 HTTPS server 的路径指向 Certbot 管理的证书名称。若有名称冲突,目录可能带后缀,以 certbot certificates 输出为准:

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

保存后先 nginx -t 再 reload。不要用只含叶子证书的文件代替需要的完整链,也不要把私钥设为全员可读。HTTP 跳转 HTTPS 应保留 ACME 验证路径例外。

容器中使用证书时,live 下文件通常指向 archive。只绑定某个当前文件或遗漏目标目录,可能导致更新后失效;挂载应保持完整目录关系,并安排容器内服务在续期后重载。

五、确认定时续期与成功后的重载

续期链路怎么验收
dry-run 不会延长生产证书的有效期。
systemctl list-timers --all | grep -i certbot || true
sudo certbot renew --dry-run

先检查当前安装方式是否已经安排 timer 或 cron,不叠加重复任务。--dry-run 用测试流程验证可续期性,不会延长当前生产证书;其 hook 行为也应按当前 Certbot 版本说明确认。

成功续期后,Nginx 需要加载新文件。可在 /etc/letsencrypt/renewal-hooks/deploy/ 下放置本例脚本,并设为 root 所有、仅管理员可修改:

#!/bin/sh
set -eu
/usr/sbin/nginx -t
/usr/bin/systemctl reload nginx

包内已有 configs/reload-nginx.sh。确认目标文件名没有被其他任务使用后,安装到 deploy hook 目录并设置执行权限:

sudo install -d -m 0755 /etc/letsencrypt/renewal-hooks/deploy
sudo install -o root -g root -m 0750 configs/reload-nginx.sh \
  /etc/letsencrypt/renewal-hooks/deploy/20-bwh-nginx.sh

不要在任意证书申请失败后也盲目重启整个服务器。重载失败要有日志和告警,不能只因 Certbot 返回成功就认为业务证书已更新。

六、需要通配符时使用 DNS-01

以 Cloudflare DNS 插件为例,使用限定目标区域、具备所需 DNS 编辑权限的 API Token,不使用拥有全账户权限的 Global API Key。把凭据保存到 root 可读的配置文件,而不是命令参数、公开 Compose 或文章截图中。

# /root/.secrets/certbot/cloudflare.ini,权限设为 600
dns_cloudflare_api_token = REPLACE_WITH_RESTRICTED_TOKEN

插件要通过与当前 Certbot 一致的安装渠道安装。确认后使用:

sudo certbot certonly --dns-cloudflare \
  --dns-cloudflare-credentials /root/.secrets/certbot/cloudflare.ini \
  -d example.com -d '*.example.com'

通配符需要引号避免 shell 展开,且 *.example.com 不自动覆盖 example.com。DNS 权限变更、Token 过期或迁出该 DNS 提供商后,都要重新测试续期。

七、怎样验证用户实际拿到的证书

# 外部终端执行,替换为真实域名
openssl s_client -connect site.example.com:443 \
  -servername site.example.com -showcerts </dev/null

核对证书链、域名、验证结果和日期。经过 CDN 时,外部访问可能拿到边缘证书,还要通过受控方式单独检查源站;两份证书不能混为一谈。Full (strict) 对源站也有证书要求。

根据实际证书的 notAfter 设置告警,不固定认为每一张证书都是同一种有效期。可以采用剩余 30/14/7 天的分级提醒作为起点,并把模拟续期失败也纳入告警。

八、续期失败先查这几处

续期失败的常见方向
不要靠连续重复申请解决所有验证错误。

核对 DNS 中所有 A/AAAA、CAA 是否允许所选 CA,外网 80 验证路径、CDN 的缓存和跳转、私有认证、文件属主和磁盘空间。最近若加了源站访问限制,确认没有同时挡住 HTTP 验证。

证书和关键配置可以随 独立备份 保存,但私钥必须加密或限制访问。旧机器退役前检查续期任务是否也迁移到新机,不要只搬网站文件。

常见问题

renew --dry-run 通过后就不用管了吗?

还要确认调度存在、部署 hook 正常,并从实际入口观察证书到期时间。

证书文件更新了,浏览器为什么还是旧证书?

可能服务未重载、访问了其他节点、CDN 边缘证书不同,或容器绑定的是旧文件。

通配符证书包含根域名吗?

不自动包含。需要根域名时,应把根域名也列入证书申请和验证。

相关阅读

参考资料

  1. Let’s Encrypt:HTTP-01 与 DNS-01
  2. Certbot:证书、续期与 deploy hook
  3. Certbot DNS Cloudflare:最小权限 Token
  4. Cloudflare:Full (strict) 回源校验

微信订阅号

VPS收割者微信公众号

QQ群(全员禁言)

VPS收割者QQ群(全员禁言)