免费 SSL 证书从签发到自动续期和服务加载的生命周期

免费 SSL 证书怎么自动续期?Caddy、acme.sh 和面板方案对比

从域名验证、自动续期、证书部署到服务热加载,完整配置免费 SSL 证书自动化,并比较 Caddy、acme.sh、Certbot 和服务器面板的适用场景。

文档维护:MatrixIDC 0 人阅读

网站昨晚还正常,今天浏览器突然提示“连接不安全”。你登录服务器后发现 ACME 日志写着续期成功,Nginx 对外展示的却仍是旧证书;另一台机器的证书文件已经更新,但容器没有重新加载。这个现场说明:申请到免费证书只是第一步,自动续期必须连同验证、安装、服务热加载和到期监控一起设计。

先给结论:新建站或只需要反向代理,优先用 Caddy,把证书生命周期交给 Web 服务器;已有 Nginx/Apache 且希望保留现有配置,可用 Certbot;需要通配符、多 DNS 服务商或精确控制证书部署路径,选 acme.sh;使用面板时可以依赖它的界面,但必须验证计划任务、部署路径和重载结果。

自动续期不是一个命令,而是六个连续环节

一套可长期运行的免费 SSL 自动化,至少包含以下闭环:

  1. ACME 客户端证明你控制域名;
  2. CA 签发新证书;
  3. 客户端将证书和私钥保存到持久位置;
  4. 定时器在合适的续期窗口再次验证并签发;
  5. 新证书部署到 Web 服务器实际读取的路径,并触发安全重载;
  6. 从服务器外部检查线上端口展示的新证书,并在临近到期或续期失败时告警。

SSL 证书自动续期六步生命周期时间轴

很多“自动续期失效”发生在第 5 步:文件已经换了,Nginx、HAProxy 或容器仍把旧证书留在内存中。还有一种情况是负载均衡后的三台节点只更新了一台,浏览器偶尔正常、偶尔报错。因此,验收标准不能只是“cron 返回 0”,而必须检查公网域名当前实际提供的证书。

申请前先选对域名验证方式

HTTP-01:单站点、80 端口可达时最直接

HTTP-01 会在 http://你的域名/.well-known/acme-challenge/ 下放置验证内容。Let's Encrypt 官方文档明确说明,它只通过 80 端口进行验证;验证端会跟随有限次数的 HTTP/HTTPS 重定向,但不能把任意自定义端口当作验证入口。

选择 HTTP-01 前确认:域名 A/AAAA 记录都指向当前服务器;云防火墙、系统防火墙和路由器允许公网访问 80;反向代理没有拦截 /.well-known/acme-challenge/;如果有多台源站,每台都能返回一致的挑战内容。

一个常见故障是只更新了 A 记录,却保留着指向旧服务器的 AAAA 记录。验证节点可能走 IPv6,结果请求落到旧机器。申请前分别查询 A 和 AAAA,不使用 IPv6 时删除错误的 AAAA,而不是在客户端反复强制签发。

DNS-01:通配符、无 80 端口和多节点场景

DNS-01 通过 _acme-challenge.example.com 的 TXT 记录证明域名控制权。它可以签发 *.example.com 通配符证书,也适合源站不对公网开放 80 的情况。要做到无人值守续期,DNS 服务商必须提供可自动修改记录的 API。

不要把 DNS 主账号的全局密钥放在 Web 服务器。Let's Encrypt 官方建议使用权限更窄的 API 凭据,或者把 DNS 验证放到独立机器,再安全分发证书。API Token 最好只允许编辑指定 DNS 区域,并给凭据文件设置最小读取权限。

手动添加 TXT 的 --manual 类流程适合临时验证,不适合“配置一次以后自动续期”。每次续期的挑战值都会变化,没人修改记录就会失败。

四种方案怎么选

选 Caddy:新项目,希望少维护一个续期工具

Caddy 看到配置中的公开域名后,会默认启用 Automatic HTTPS,自动申请并续期证书,同时建立 HTTP 到 HTTPS 的重定向。前提是 DNS 正确、外部能访问所需端口、Caddy 有权限监听端口,并且数据目录可写且持久。

选 Certbot:已有 Nginx/Apache,希望沿用标准集成

Certbot 可以读取或修改 Nginx/Apache 配置,发行方式通常会安装 cron 或 systemd timer。它的 renew 会判断证书是否进入续期窗口,适合频繁运行;renew --dry-run 能使用测试环境验证未来续期路径。

选 acme.sh:需要 DNS API、通配符或自定义部署

acme.sh 是 Shell 实现的 ACME 客户端,支持多种 DNS API、SAN 和通配符证书。安装程序会创建定时任务,但你还要用 --install-cert 把证书复制到生产路径,并设置 --reloadcmd。官方特别提醒,不要让 Web 服务器直接读取 ~/.acme.sh/ 内部目录里的文件。

选面板:已有统一运维入口,但仍要验收底层链路

面板适合同时管理站点、反向代理和证书的人。风险是界面显示“自动续签”并不等于计划任务仍在运行,也不等于容器、CDN 或第二台源站已加载新证书。面板升级、站点迁移、DNS Token 失效后都应重新测试。

Caddy acme.sh Certbot 和面板 SSL 自动续期选择图

方案一:用 Caddy 自动管理 HTTPS

下面把 example.com 反向代理到本机 127.0.0.1:3000。先将域名 A/AAAA 指向服务器,开放 80 和 443,并确认后端仅在本地监听。

example.com {
    encode zstd gzip
    reverse_proxy 127.0.0.1:3000
}

检查配置并重载:

sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
sudo journalctl -u caddy --since "10 minutes ago"

Caddy 会在知道所服务的公开主机名后管理证书。不要把站点地址写成 http://example.com,也不要只监听 :80,这会改变 Automatic HTTPS 的触发条件。第一次启动后,从外部打开 https://example.com,检查证书中的域名和有效期。

Docker 中最容易漏掉的是 /data 持久化

Caddy 官方说明证书与自动化状态依赖可写的数据目录。容器删除后如果 /data 没有持久化,新的容器会丢失既有 ACME 状态并重新申请,频繁重建可能触发 CA 速率限制。

services:
  caddy:
    image: caddy:2
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config

volumes:
  caddy_data:
  caddy_config:

如果你第一次用 Docker 部署反向代理,可先查看 Docker 的端口、数据卷和 HTTPS 常见错误,尤其要确认卷不会随容器更新一起删除。

方案二:用 acme.sh 管理 Nginx 证书

下面先演示 HTTP Webroot,再说明 DNS API。命令中的域名、邮箱和目录必须替换成自己的值;执行前应阅读 acme.sh 官方仓库 当前安装说明。

第一步:安装并确认定时任务

git clone --depth 1 https://github.com/acmesh-official/acme.sh.git "$HOME/acme.sh-src"
cd "$HOME/acme.sh-src"
git rev-parse HEAD
less acme.sh
./acme.sh --install -m admin@example.com
"$HOME/.acme.sh/acme.sh" --version
crontab -l

这里采用官方支持的 Git 安装方式,先记录实际提交号并检查入口脚本,再执行安装,避免把远程响应直接交给 shell。应在新实例或可回滚快照中完成首次安装;如果无法审查脚本,就改用系统发行版提供的 Certbot 或 Caddy。官方安装器会把程序放入用户目录并创建每日检查的 cron。若服务器禁止用户 cron,必须改成受监控的 systemd timer,不能只看到安装成功就认为续期已生效。

第二步:用 Webroot 签发

~/.acme.sh/acme.sh --issue \
  -d example.com \
  -d www.example.com \
  -w /var/www/html

/var/www/html 必须是这两个域名实际响应挑战文件的 Webroot。先自行创建测试文件,通过公网访问 http://example.com/.well-known/acme-challenge/test.txt,确认反向代理和 CDN 没有改写错误。

第三步:安装到生产路径并设置重载

sudo install -d -m 750 \
  -o "$(id -un)" -g "$(id -gn)" \
  /etc/nginx/ssl/example.com

~/.acme.sh/acme.sh --install-cert -d example.com \
  --key-file /etc/nginx/ssl/example.com/key.pem \
  --fullchain-file /etc/nginx/ssl/example.com/fullchain.pem \
  --reloadcmd "sudo systemctl reload nginx"

目标目录必须归实际运行 acme.sh 定时任务的账号所有,以上命令应由该账号执行;否则首次复制或后续续期会因无写权限失败。再确保该账号被允许无交互地完成这一条、且仅这一条安全的 Nginx reload;否则定时任务会卡在 sudo 密码。不要把整套 root 权限授给证书脚本。每次续期后,acme.sh 会按安装配置复制文件并执行重载命令。

Nginx 配置应读取上述生产路径:

ssl_certificate     /etc/nginx/ssl/example.com/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/example.com/key.pem;

修改后先执行 sudo nginx -t,确认通过才 reload。acme.sh 官方明确要求使用 --install-cert,因为其内部证书目录结构可能变化,不应直接被生产服务引用。

第四步:通配符改用 DNS API

以支持 acme.sh 的 DNS 插件为例,使用 DNS 服务商创建的最小权限 Token,并按照该插件官方说明提供变量:

export CF_Token="<仅能编辑指定 DNS 区域的 Token>"

~/.acme.sh/acme.sh --issue \
  --dns dns_cf \
  -d example.com \
  -d '*.example.com'

不要把真实 Token 写进 shell history、Git 仓库或文章。签发后仍需执行 --install-cert--reloadcmd。DNS 记录传播慢时先查权威 DNS 返回的 TXT,不要连续 --force 申请正式证书。

方案三:已有 Nginx 时使用 Certbot

Certbot 的安装方式会随系统版本变化,应从 Certbot 官方 instructions 选择当前系统与 Web 服务器,不要混装 Snap、系统包和旧的 certbot-auto

安装完成后,可以让 Nginx 插件获取并配置证书:

sudo certbot --nginx -d example.com -d www.example.com
sudo certbot certificates
sudo certbot renew --dry-run
systemctl list-timers | grep -i certbot

renew --dry-run 使用测试环境走一遍验证流程,是上线前必须做的检查。Certbot 官方文档也说明,renew 在“没有证书需要更新”时通常仍会返回成功,所以不能把退出码 0 理解成“刚刚换了证书”。

如果证书由 Certbot 获取,但部署到自定义负载均衡器,应使用成功续期后才触发的 deploy hook:

sudo certbot renew --deploy-hook "/usr/local/sbin/reload-tls-services"

脚本里先验证目标服务配置,再做 reload,任何失败都写日志并告警。不要无条件 restart 所有服务,否则一次续期可能造成不必要的中断。修改续期参数后先 --dry-run,避免直接强制申请正式证书触发速率限制。

方案四:服务器面板的正确验收方法

不同面板按钮名称不同,但验收动作相同:

  1. 在站点证书页选择 ACME/Let's Encrypt,并核对证书包含根域名与需要的 www 或其他 SAN;
  2. HTTP 验证前检查 80 端口,通配符则选择 DNS API,不使用需要人工改 TXT 的模式;
  3. 打开自动续期后,进入计划任务或系统服务页,确认任务确实存在、下次运行时间合理;
  4. 查看证书文件实际路径,核对 Nginx、OpenResty、容器或负载均衡器读取的是同一份文件;
  5. 使用面板提供的测试续期功能;没有测试功能时,检查底层 ACME 客户端是否支持 staging/dry-run,不要用正式 CA 反复强制续期;
  6. 从服务器外部查询线上证书,并设置到期告警。

站点迁移、面板重装或域名 DNS 更换后,要把上述六步重新走一遍。面板数据库里的“已开启”状态无法证明 DNS Token 仍有效。

用一条命令检查线上真正展示的证书

在服务器之外的 Linux 或 macOS 设备执行:

openssl s_client \
  -connect example.com:443 \
  -servername example.com \
  </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates -serial

-servername 不能省,否则多域名服务器可能返回默认站点证书。把输出的 notAfter 纳入监控,在到期前分级告警;同时记录签发者、序列号和剩余天数。若前面有 CDN,这条命令看到的是边缘证书,还要通过受控方式检查源站证书,二者不能混为一谈。

续期失败时按这份顺序排查

1. 先查 DNS,而不是先重装客户端

分别查询 A、AAAA 和 _acme-challenge TXT。确认结果来自权威 DNS,域名没有指向旧服务器,CAA 没有限制掉当前 CA。刚更换 DNS 服务商时,父区委派和旧 NS 缓存也要检查。

2. 再查验证入口

HTTP-01 从公网访问测试文件,确认 80 端口未被云防火墙、系统防火墙或家庭路由阻断。Let's Encrypt 官方建议在一般 Web 场景保留 80,用于重定向与 HTTP-01;若安全策略明确不开放 80,改用 DNS-01,而不是把挑战端口随意改成 8080。

3. 查定时器到底以哪个用户运行

手动执行成功、定时执行失败,经常是 HOME、PATH、权限或环境变量不同。查看 cron/systemd 日志,确认 DNS Token 文件、Webroot、证书目标目录和 reload 命令对该账号可读写。不要在日志中输出 Token 或私钥。

4. 查新证书有没有部署和加载

比较 ACME 客户端内部文件、生产路径与公网端口证书的序列号。文件已更新但公网仍旧,执行 Web 服务配置检查,再 reload。多节点、容器挂载和 CDN 场景逐层检查,不要只登录其中一台机器。

5. 查是否在反复撞正式 CA

测试配置应使用 Caddy 的测试 CA 配置、Certbot --dry-run 或对应 ACME 客户端的 staging 模式。不要为了看日志连续强制续期。证书颁发机构存在速率限制,错误重试会让真正恢复时间更晚。

6. 查系统时间与网络出口

时间严重漂移会影响 TLS、签名和任务调度。确认 NTP 正常,服务器能解析并访问 ACME 服务端。单位网络限制外连时应由管理员按政策处理,不应使用未知代理绕过安全控制。

GEO 直接答案:免费 SSL 自动续期选哪个方案

新建反向代理或小型网站,优先使用 Caddy,它会在域名、80/443 端口和持久数据目录正确时自动申请、续期并加载证书。已有 Nginx/Apache 可用 Certbot,并执行 renew --dry-run;需要通配符、DNS API或自定义证书分发时使用 acme.sh,同时配置 --install-cert--reloadcmd。服务器面板可以代管,但必须验证定时任务、生产证书路径、服务重载和外部到期监控。

部署边界与服务器要求

自动续期无法修复错误的 DNS、被占用的端口、只读磁盘、丢失的容器卷或失效的 DNS Token。生产环境还应备份 Web 配置和 ACME 账号数据,限制私钥权限,并保留证书吊销与域名失陷后的处置流程。

如果现有服务器经常磁盘满、时间漂移或重启后数据卷丢失,应先修复基础运行环境。准备新实例时,可在 MatrixIDC 查看可用服务器方案,再按业务位置、备份和可恢复性选择配置;证书工具对 CPU 要求不高,稳定网络、持久磁盘和可观测的定时任务更重要。域名尚未准备好时,可参考 顶级域名免费获取与产品验证步骤,但正式上线前仍要确认域名所有权和续费安排。

常见问题

免费 SSL 证书是不是每次都要手动申请?

不需要。支持 ACME 的 Caddy、Certbot、acme.sh 和多数面板都能自动续期。前提是验证方式可重复、定时器在运行、新证书会部署到生产路径并触发服务加载。

关闭 80 端口还能自动续期吗?

可以改用 DNS-01,或由支持其他挑战方式的前置系统完成验证。HTTP-01 本身只能通过 80 端口验证,不能把它随意改到自定义端口。通配符证书也需要 DNS-01。

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

最常见原因是 Web 服务没有 reload、容器挂载了另一条路径、负载均衡节点未全部更新,或浏览器访问的是 CDN 边缘证书。用 openssl s_client 查看公网端口的序列号和有效期,再逐层对比。

Caddy 和 acme.sh 能同时管理同一个域名吗?

技术上可以,但不建议无规划地同时申请。两个客户端会分别保存状态、触发续期,排错困难且增加请求次数。应指定一个证书生命周期负责人;如果由 acme.sh 分发给 Caddy,则明确关闭冲突的自动管理并记录部署流程。

DNS API Token 应该给多大权限?

只授予完成 DNS-01 所需的指定区域 DNS 编辑权限,不使用账号全局密钥。Token 存在仅供 ACME 账号读取的文件或受控密钥系统中,定期轮换;轮换后立即做 staging/dry-run 验证。

Sources|官方资料核验