香港服务器上的 Let's Encrypt 证书如何配置自动续期并验证结果?
香港服务器上的 Let’s Encrypt 证书可以通过 Certbot 完成签发,再由 systemd 定时任务检查续期;证书更新后,使用部署钩子执行 nginx -t 和平滑重载,让新证书进入实际服务。验证时必须同时检查“续期流程是否成功”和“公网 HTTPS 是否正在提供正确证书”,不能只看定时器是否启用。
下面以 Ubuntu 22.04/24.04 LTS、Nginx、APT 安装的 Certbot、单域名 HTTP-01 验证为操作环境。上线目标是完成证书签发、HTTPS 配置、自动续期、续期演练及远端验证。国密证书另设适配说明:Let’s Encrypt 不能直接签发 SM2 国密证书,不能把普通 HTTPS 的自动续期流程原样套用到国密服务。
围绕香港网站与接口服务的 HTTPS 部署,A5数据提供香港物理服务器租用,涵盖常规建站、Xeon Gold 与 AMD EPYC 等资源档位,配备 SSD 或 NVMe 存储,并提供不同套餐的 CN2 与国际带宽选择。这些计算、存储和网络资源可承载 Nginx 站点、业务后台及数据库,为证书管理、自动续期任务与业务服务协同运行提供基础设施支持。
一、准备条件与部署边界
1. 确认域名、端口和证书终止位置
执行前应满足以下条件:
- 已取得服务器的管理员权限,并保留 SSH 或控制台恢复入口。
- 域名由自己管理,A 记录指向香港服务器公网 IPv4。
- 如果存在 AAAA 记录,对应 IPv6 也必须能访问正确站点。
- 安全组、主机防火墙及上游设备允许公网访问 TCP 80、443。
- 服务器能够通过 HTTPS 访问 ACME 服务,系统时间准确。
- 已确定证书由哪一层提供:源站 Nginx、负载均衡,还是 CDN 边缘节点。
HTTP-01 验证要求证书机构从公网访问:
http://域名/.well-known/acme-challenge/验证文件
使用 HTTP-01 自动续期时,80 端口不仅要在首次签发时开放,后续验证也要可达。普通请求可以跳转到 HTTPS,但验证目录应直接返回文件,避免被登录验证、访问控制或业务跳转拦截。
香港服务器的地理位置不会改变 ACME 的验证机制。需要检查的是公网可达性、DNS 指向和访问策略,而不是证书是否必须由香港本地机构签发。
如果需要通配符证书,或无法开放 80 端口,应改用 DNS-01。要实现无人值守续期,通常需要 DNS 服务商 API、相应 Certbot 插件及权限受限的凭据;手动添加 TXT 记录不等于已经配置自动续期。
2. 检查运行环境并备份
以下命令适用于 Ubuntu,先进入 root 管理终端:
sudo -i
cat /etc/os-release
command -v nginx || true
command -v certbot || true
systemctl status nginx --no-pager
timedatectl status
ss -lntp | grep -E ':(80|443)\b' || true
若已有 Certbot,再检查来源和版本:
certbot --version
dpkg -l certbot
snap list certbot 2>/dev/null || true
本文统一使用 APT 版本。已有其他安装方式时,应先核对其服务、配置路径和定时任务,不要再混装一套 Certbot。80、443 若被其他服务占用,也不要直接停止进程,应先确认业务归属。
配置改动前备份:
BACKUP="/root/a5https-backup-$(date +%Y%m%d-%H%M%S)"
install -d -m 700 "$BACKUP"
if [ -d /etc/nginx ]; then
cp -a /etc/nginx "$BACKUP/"
fi
if [ -d /etc/letsencrypt ]; then
cp -a /etc/letsencrypt "$BACKUP/"
fi
printf '备份目录:%s\n' "$BACKUP"
备份包含证书私钥,应保存在仅管理员可访问的位置,不要放入网站目录、代码仓库或工单附件。
后续命令在同一个 root 终端中执行。设置真实域名和可接收通知的邮箱:
DOMAIN="example.com"
EMAIL="ops@example.com"
example.com 仅为示例,签发前必须替换。
二、逐步签发证书并上线 HTTPS
1. 安装依赖,核验 DNS
如果服务器尚未安装相应软件:
apt update
apt install -y nginx certbot curl openssl ca-certificates dnsutils
nginx -v
certbot --version
openssl version
安装 Nginx 可能启动服务并监听 80 端口。已有生产环境应在变更窗口执行,不要顺带进行无关的软件升级。
检查 DNS:
dig +short A "$DOMAIN"
dig +short AAAA "$DOMAIN"
dig CAA "$DOMAIN"
预期结果:
- A 记录指向目标服务器,或指向能够正确转发验证请求的入口。
- AAAA 若存在,不能指向另一台未配置验证目录的服务器。
- 如果设置了 CAA,应允许 Let’s Encrypt 签发;还要留意父域继承的限制。
域名经过 CDN 时,上述地址通常是边缘节点地址。此时需要保证验证路径能够到达源站,且不被缓存旧响应或附加访问限制。
2. 创建 HTTP 验证站点
下面配置适用于新建的单域名站点。已有业务站点应只合并验证目录配置,保留原有静态文件、反向代理和访问控制逻辑,不要覆盖整个虚拟主机。
先确认目标配置文件不存在:
SITE_CONF="/etc/nginx/sites-available/le-${DOMAIN}"
test ! -e "$SITE_CONF" && echo "可创建新配置"
test ! -e "/etc/nginx/sites-enabled/le-${DOMAIN}" && echo "可创建启用链接"
任一目标已存在时,应停止并检查,不继续执行下面的覆盖写入命令。
创建目录和演示页面:
install -d -m 755 /var/www/acme/.well-known/acme-challenge
install -d -m 755 "/var/www/le-${DOMAIN}"
printf 'HTTPS deployment check\n' > "/var/www/le-${DOMAIN}/index.html"
printf 'acme-probe-ok\n' > /var/www/acme/.well-known/acme-challenge/probe.txt
写入 HTTP 配置:
cat > "$SITE_CONF" <<EOF
server {
listen 80;
listen [::]:80;
server_name ${DOMAIN};
root /var/www/le-${DOMAIN};
index index.html;
location ^~ /.well-known/acme-challenge/ {
root /var/www/acme;
default_type text/plain;
try_files \$uri =404;
}
location / {
try_files \$uri \$uri/ =404;
}
}
EOF
ln -s "$SITE_CONF" "/etc/nginx/sites-enabled/le-${DOMAIN}"
nginx -t && systemctl reload nginx
若系统禁用了 IPv6,且 nginx -t 因 IPv6 监听失败,应移除对应的 listen [::] 行后重试。不要忽略配置检测错误直接重启服务。
检查本机虚拟主机匹配:
curl --resolve "${DOMAIN}:80:127.0.0.1" \
"http://${DOMAIN}/.well-known/acme-challenge/probe.txt"
再从服务器之外的终端检查:
curl -i "http://example.com/.well-known/acme-challenge/probe.txt"
成功时应返回 HTTP 200,正文为:
acme-probe-ok
本机成功、外部失败,说明问题更可能在 DNS、安全组、防火墙、CDN 或上游网络,而不是文件本身。仅在服务器上访问自己的域名,不能完整代替公网验证。
3. 使用 webroot 模式签发
执行:
certbot certonly \
--webroot \
--webroot-path /var/www/acme \
--cert-name "$DOMAIN" \
-d "$DOMAIN" \
--email "$EMAIL" \
--agree-tos \
--non-interactive
这里的 certonly 只申请证书,不让 Certbot 自动修改 Nginx 配置;webroot 模式也不需要停止 Nginx。
成功后检查:
certbot certificates
openssl x509 \
-in "/etc/letsencrypt/live/${DOMAIN}/cert.pem" \
-noout -subject -issuer -dates -ext subjectAltName
应能看到正确域名、签发者和有效期。具体有效期以实际证书为准,不在监控脚本里固定假定某个天数。
文件用途如下:
| 路径 | 用途 | Nginx 配置方式 |
|---|---|---|
live/域名/fullchain.pem | 站点证书及中间证书链 | 用于 ssl_certificate |
live/域名/privkey.pem | 证书私钥 | 用于 ssl_certificate_key |
live/域名/cert.pem | 站点叶子证书 | 用于检查信息 |
renewal/域名.conf | 续期参数 | 由 Certbot 管理,不随意手改 |
Nginx 应引用 live 下的稳定路径,不要写死 archive 中带编号的文件,也不要把证书复制到另一个目录后忘记同步续期结果。
4. 切换 HTTPS,保留 HTTP 验证入口
先保存当前可用的 HTTP 配置:
cp -a "$SITE_CONF" "$BACKUP/le-${DOMAIN}.http.conf"
下面命令会覆盖本教程刚创建的配置文件,影响该域名的访问方式;已有业务不能直接照抄覆盖。
cat > "$SITE_CONF" <<EOF
server {
listen 80;
listen [::]:80;
server_name ${DOMAIN};
location ^~ /.well-known/acme-challenge/ {
root /var/www/acme;
default_type text/plain;
try_files \$uri =404;
}
location / {
return 301 https://${DOMAIN}\$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name ${DOMAIN};
ssl_certificate /etc/letsencrypt/live/${DOMAIN}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
root /var/www/le-${DOMAIN};
index index.html;
location / {
try_files \$uri \$uri/ =404;
}
}
EOF
nginx -t && systemctl reload nginx
先检测再平滑重载,可避免因新配置语法错误而主动停止现有服务。此阶段不急于启用长期 HSTS,以免回滚到 HTTP 时客户端仍强制访问 HTTPS。
验证:
curl -I "http://${DOMAIN}/"
curl -I "https://${DOMAIN}/"
curl -i "http://${DOMAIN}/.well-known/acme-challenge/probe.txt"
预期分别为:普通 HTTP 请求跳转到 HTTPS、HTTPS 页面返回 200、验证目录继续直接返回 200。
三、配置自动续期与部署钩子
1. 检查 systemd 定时器
Ubuntu 的 APT 安装方式通常提供 certbot.timer。先查看实际配置:
systemctl cat certbot.timer
systemctl cat certbot.service
systemctl list-timers --all | grep certbot
确认服务调用的是预期 Certbot,再启用:
systemctl enable --now certbot.timer
systemctl status certbot.timer --no-pager
systemctl list-timers --all | grep certbot
应能看到定时器处于等待状态,并有下一次执行时间。若系统不存在该单元,应检查安装来源,不要直接复制其他发行版的定时器配置。
定时器按周期检查证书,但不会每次都重新签发。是否进入续期窗口由 Certbot、证书信息及可用的 ACME 续期机制决定。因此,日志显示“暂不需要续期”通常是正常结果。
不要再同时添加重复的 cron 任务,避免多套调度并行、锁冲突和日志混乱。
2. 添加“续期成功后重载 Nginx”的钩子
证书文件更新,不代表 Nginx 已经载入新证书。应使用部署钩子,而不是无论续期是否发生都重启服务。
确认脚本路径未被占用后创建:
install -d -m 755 /etc/letsencrypt/renewal-hooks/deploy
HOOK="/etc/letsencrypt/renewal-hooks/deploy/20-reload-nginx"
test ! -e "$HOOK" && echo "可创建部署钩子"
如果文件已存在,先检查内容,不覆盖既有运维逻辑。新建脚本:
cat > "$HOOK" <<'EOF'
#!/bin/sh
set -eu
/usr/sbin/nginx -t
/usr/bin/systemctl reload nginx
EOF
chmod 750 "$HOOK"
权限调整仅针对这个脚本,让管理员可执行;不要递归放宽 /etc/letsencrypt 的权限。私钥不应向普通用户开放。
手动验证钩子:
"$HOOK"
systemctl is-active nginx
预期配置检查通过,Nginx 仍为 active。若配置检测失败,脚本会停止,不执行重载;此时应修复 Nginx 配置,而不是删除检测步骤。
该目录中的钩子可能在其他证书成功续期时也执行。若服务器承载多个服务,应评估是否需要按 RENEWED_LINEAGE 区分证书归属,避免无关服务重复重载。
3. 执行续期演练
先运行:
certbot renew --cert-name "$DOMAIN" --dry-run
--dry-run 用于测试验证和续期流程,不替换正式服务证书。合理的成功结果会包含模拟续期通过的信息,但具体文案随版本变化。
默认的模拟续期不应被视为已经验证部署钩子。检查当前版本是否支持显式执行:
certbot --help all | grep -- '--run-deploy-hooks'
若支持,再运行:
certbot renew \
--cert-name "$DOMAIN" \
--dry-run \
--run-deploy-hooks
此操作会在模拟续期成功后执行部署钩子,Nginx 会平滑重载;测试中使用的临时证书不会因此成为线上服务证书。若版本不支持该选项,则分别完成 dry-run 和前面的手动钩子测试,并在首次正式续期后再次核验远端证书。

不要用反复执行 --force-renewal 来证明自动续期,避免触发签发限制。
四、从公网检查结果,并按层次处理异常
1. 区分四种验证结果
自动续期验收应覆盖以下四层:
| 验证层 | 检查对象 | 成功意味着什么 |
|---|---|---|
| 调度层 | systemd 定时器 | 续期检查会自动触发 |
| 签发层 | dry-run、Certbot 日志 | 验证路径和续期参数可用 |
| 部署层 | 钩子、Nginx 配置检测及重载 | 更新后能够载入证书 |
| 服务层 | 外部 TLS 连接 | 访问者获得正确证书及证书链 |
前面三层成功,仍不能省略服务层检查。例如,域名可能指向另一台服务器,或者公网证书实际由 CDN 提供。
2. 检查证书链、域名及线上证书
从外部终端执行,将域名替换为实际值:
openssl s_client \
-connect example.com:443 \
-servername example.com \
-verify_hostname example.com \
-verify_return_error \
</dev/null
在系统信任库正常的条件下,应看到证书验证通过,例如 Verify return code: 0 (ok),且没有主机名不匹配错误。
检查服务器本地证书:
openssl x509 \
-in "/etc/letsencrypt/live/${DOMAIN}/cert.pem" \
-noout -serial -dates -fingerprint -sha256
提取公网提供的证书:
openssl s_client \
-connect "${DOMAIN}:443" \
-servername "$DOMAIN" \
</dev/null 2>/dev/null |
openssl x509 -noout -serial -dates -fingerprint -sha256
如果 TLS 直接终止在这台 Nginx,本地与公网证书的序列号、指纹应对应。若使用 CDN 或负载均衡,两者可能不同,应分别检查边缘证书和源站证书,不能据此直接判定续期失败。

要绕开 DNS 路径检查源站,可从外部终端执行:
curl --resolve example.com:443:203.0.113.10 \
-I https://example.com/
其中 203.0.113.10 是示例地址,必须替换为源站公网 IP。该方式保留域名和 SNI,比直接访问 https://IP地址/ 更适合检查域名证书。
3. 查看日志并设置到期告警
检查续期服务及日志:
journalctl -u certbot.service --since "7 days ago" --no-pager
tail -n 100 /var/log/letsencrypt/letsencrypt.log
手动执行的 Certbot 命令不一定出现在该 systemd 服务日志中,因此两处都应查看。
可以检查本地证书是否会在 14 天内到期:
openssl x509 \
-in "/etc/letsencrypt/live/${DOMAIN}/cert.pem" \
-noout -checkend 1209600
echo $?
这里 1209600 为 14 × 24 × 60 × 60 秒。返回 0 表示证书不会在该窗口内到期;非零时需要查看错误信息,区分即将到期、文件缺失或读取失败。
告警窗口应结合证书实际寿命和团队响应时间设置。建议同时监控公网证书到期时间及续期任务失败,不能只监控本地文件。
4. 常见失败按由外到内排查
| 现象 | 优先检查 | 处理方向 |
|---|---|---|
| 验证连接超时 | DNS、80 端口、安全组、上游访问策略 | 确认公网能访问验证路径 |
| 验证返回 404 | 虚拟主机匹配、webroot、CDN 回源 | 确认请求进入正确目录 |
| IPv4 可用但验证失败 | AAAA 记录及 IPv6 站点 | 修复 IPv6,或在确认无需 IPv6 后调整 DNS |
| 返回 403、登录页或业务页面 | 认证、重写、WAF、验证路径缓存 | 仅对挑战路径作必要放行 |
| CAA 拒绝签发 | 当前域名及父域 CAA | 经授权修改签发策略 |
| dry-run 成功,线上仍是旧证书 | 钩子、Nginx 路径、实际 TLS 终止层 | 检查重载及边缘证书配置 |
nginx -t 失败 | 配置语法、文件存在性、证书与私钥 | 修复后重新检测,勿强行重启 |
| 提示锁被占用 | 正在运行的 Certbot、重复调度 | 等待任务结束,清理重复调度 |
| 签发次数受限 | 多次正式失败或强制续期 | 先修复验证,再使用测试流程排查 |
不要为了排障清空防火墙、将私钥改成全员可读,或直接删除 Certbot 锁文件。若必须调整访问策略,应保存原规则,只增加必要范围,并确认 SSH 管理入口不受影响。
五、国密证书如何与这套流程适配
Let’s Encrypt 自动续期和国密证书适配是两项不同的工作。Certbot 不能通过增加一个参数,将普通证书申请变成 SM2 国密证书申请;部署在香港服务器上,也不会改变这一边界。
| 对象 | 获取与更新方式 | 服务端要求 | 验证重点 |
|---|---|---|---|
| Let’s Encrypt 普通 HTTPS 证书 | Certbot 与 ACME 续期 | 支持相应证书类型的 Nginx/TLS 环境 | 域名、证书链、到期时间、线上证书 |
| SM2 国密证书 | 支持国密的 CA 及其申请、更新流程 | 明确支持目标算法和协议的服务端 | 算法、用途、信任链、客户端兼容 |
| TLCP 等国密服务 | 按具体 CA 和产品方案实施 | 相应国密网关或服务器实现 | 协议协商,以及方案要求的签名、加密证书配套关系 |
Ubuntu 常规仓库中的 Nginx/OpenSSL 组合不能被默认视为已经满足目标国密服务要求。配置了 TLS 1.3,也不等于启用了国密算法。
实际部署前,需要向 CA 和服务端产品方确认:
- 提供的是何种证书、协议及算法组合。
- 是否需要分别配置签名证书和加密证书。
- 客户端是否支持该协议并信任相应证书链。
- 是否提供自动更新 API、专用客户端或人工更新流程。
- 普通浏览器访问和国密客户端访问是否需要独立入口。
业务同时面向普通浏览器和国密客户端时,可以将普通 HTTPS 与国密入口分开部署,再分别验证。不要把 SM2 证书直接填入普通 Nginx 的证书路径后,就认定适配完成;普通 RSA/ECDSA 多证书配置也不等同于国密双证书配置。
如果国密 CA 提供自动化接口,应建立独立的更新流程:申请或下载证书、校验证书用途及密钥配对、检查链完整性、暂存新文件、检测服务配置、受控切换并重载、使用目标国密客户端验证。更新失败时应保留仍有效的旧证书和配置。
普通 HTTPS 可继续由 Certbot 管理,但国密证书不应手动塞入 Certbot 的 live 目录。是否满足特定合规要求,还取决于密码产品、协议实现、证书用途和应用系统要求,不能仅凭一张证书判定。
六、上线验收与回滚检查项
上线验收
- 域名 A、AAAA 与实际服务入口一致,验证路径从公网返回 200。
- HTTPS 页面可访问,域名匹配,证书链验证通过。
- HTTP 普通请求跳转到 HTTPS,挑战路径不被业务规则拦截。
- Nginx 引用
live下的证书路径,未引用固定编号或遗漏同步的副本。 certbot.timer已启用,下一次执行时间可查,没有重复调度。- dry-run 成功,部署钩子检测和重载成功。
- 已设置证书到期及续期失败告警,明确告警接收人。
- CDN、负载均衡、源站和国密入口按各自证书分别验收。
- 私钥及备份权限受控,备份路径和恢复入口已记录。
配置失败时回滚
对于本文新建站点,如果 HTTPS 配置失败,可恢复之前保存的 HTTP 配置:
cp -a "$BACKUP/le-${DOMAIN}.http.conf" "$SITE_CONF"
nginx -t && systemctl reload nginx
这会撤回该站点的 HTTPS 配置,使其回到 HTTP 演示状态。生产业务若禁止明文访问,应恢复原有的、证书仍有效的 HTTPS 配置,而不是采用此 HTTP 回滚方式。
只恢复本次修改的站点文件,不要直接用整份备份覆盖当前 /etc/nginx,以免撤销同期其他业务变更。若已启用 HSTS,还需考虑客户端缓存对 HTTP 回滚的影响。
若需要撤回新建的部署钩子,可以先将其移出执行目录,保留文件供检查:
mv /etc/letsencrypt/renewal-hooks/deploy/20-reload-nginx \
"$BACKUP/20-reload-nginx.disabled"
这只适用于本文新建且已确认不被其他业务依赖的脚本。移动后 Certbot 仍可能续期,但不会由该钩子自动重载 Nginx,应及时补上经过验证的替代部署流程。
只有确认系统中其他证书不依赖该定时器时,才考虑停止调度:
systemctl disable --now certbot.timer
systemctl status certbot.service --no-pager
停止定时器不会自动终止已经启动的续期服务,也不会撤销已签发证书。不要手动删除 live、archive 中的文件或修改其链接。确需移除证书时,应先解除所有服务引用、验证配置,再使用 Certbot 的证书管理命令处理。
回滚完成后再次检查 Nginx 状态、业务访问、实际提供的证书及到期时间。旧备份只能恢复文件状态,不能延长证书有效期;恢复服务之后,仍需安排新的续期验证窗口。
