上一篇 分享链接 返回 返回顶部

香港服务器多域名HTTPS如何部署SSL证书全自动续期并实现零中断

发布人:Minchunlin 发布时间:2026-10-05 13:13 阅读量:7

在香港服务器上实现多域名 HTTPS、证书自动续期和业务零中断,推荐采用 Ubuntu 22.04/24.04 LTS + Nginx + Certbot + systemd timer 的组合。证书使用一个包含多个域名的 SAN 证书,续期后由 Certbot 部署钩子执行 nginx -t,确认配置无误后使用 systemctl reload nginx 平滑加载新证书,而不是重启 Nginx。

下面以 example.com、www.example.com 和 api.example.com 为示例。部署前请替换为实际域名,并确保这些域名都解析到同一台香港服务器。只要 DNS、80/443 端口、ACME 验证目录和 Nginx 配置均正常,证书更新过程不会中断现有连接;但证书到期、域名解析错误或后端服务异常,仍然可能造成访问失败,因此需要配合验证和回滚。

一、适用环境与最终架构

1. 运行环境

项目示例配置
操作系统Ubuntu Server 22.04 LTS 或 24.04 LTS
Web 服务Nginx 1.22 或更高版本
证书客户端Certbot 1.x/2.x
服务管理systemd
证书验证方式HTTP-01 Webroot
示例域名example.com、www.example.com、api.example.com
HTTPS 终止位置香港服务器上的 Nginx
业务目标多域名共用 HTTPS,证书自动续期,续期后平滑加载

如果多个域名由同一个 Nginx 实例处理,可以使用一个 SAN 证书:

example.com
www.example.com
api.example.com

如果不同域名属于不同业务、不同团队,或者希望降低单张证书变更对其他域名的影响,也可以为每组域名单独申请证书。无论采用哪种方式,自动续期后的核心动作都相同:验证 Nginx 配置,然后执行平滑 reload。

2. 证书续期的工作链路

完整链路如下:

一、适用环境与最终架构 / 2. 证书续期的工作链路配图

  1. 域名 A、域名 B、域名 C 解析到香港服务器。
  2. Certbot 在 /var/www/acme/.well-known/acme-challenge/ 写入验证文件。
  3. 证书服务通过 HTTP 访问每个域名的验证地址。
  4. 证书续期成功后,Certbot 更新 /etc/letsencrypt/live/ 下的符号链接。
  5. 部署钩子执行 nginx -t。
  6. 配置检查通过后执行 systemctl reload nginx。
  7. 新建 HTTPS 连接使用新证书,已有连接由旧 worker 平滑处理。

二、部署前检查与备份

1. 确认服务器和软件版本

登录服务器后先检查系统、Nginx 和 Certbot:

cat /etc/os-release
uname -a
nginx -v
certbot --version
systemctl is-active nginx

如果尚未安装 Nginx 和 Certbot,可以在 Ubuntu 上执行:

sudo apt-get update
sudo apt-get install -y nginx certbot
sudo systemctl enable --now nginx

安装完成后再次确认:

nginx -v
certbot --version
sudo systemctl status nginx --no-pager

如果服务器上已经运行其他 Web 服务,不要直接安装或覆盖现有配置,应先确认 80 和 443 端口的占用情况:

sudo ss -lntp | grep -E ':(80|443)\s'

2. 备份现有 Nginx 配置

修改前先备份 Nginx 配置。备份目录包含站点配置和可能存在的私密参数,应限制访问权限:

sudo install -d -m 700 /root/nginx-backup
sudo cp -a /etc/nginx /root/nginx-backup/nginx-before-$(date +%Y%m%d-%H%M%S)

如果服务器已经存在 /etc/letsencrypt,还应备份证书配置和私钥:

sudo install -d -m 700 /root/letsencrypt-backup
sudo cp -a /etc/letsencrypt /root/letsencrypt-backup/letsencrypt-before-$(date +%Y%m%d-%H%M%S)
sudo chmod -R go-rwx /root/letsencrypt-backup

私钥备份不能放在普通网站目录,也不要上传到代码仓库。

3. 检查 DNS 解析

在服务器或运维终端执行:

dig +short A example.com
dig +short A www.example.com
dig +short A api.example.com

dig +short AAAA example.com
dig +short AAAA www.example.com
dig +short AAAA api.example.com

三个域名的 A 记录应指向当前服务器公网 IPv4 地址。

如果存在 AAAA 记录,必须确认服务器确实配置了对应 IPv6,并且 Nginx 监听 IPv6。如果 AAAA 记录指向旧服务器或其他地址,HTTP-01 验证可能随机失败。此时应修正 AAAA 记录,或者在确认业务不使用 IPv6 的情况下移除错误的 AAAA 记录。

DNS 检查没有通过时,不要继续反复申请证书,否则可能触发证书服务的频率限制。

4. 检查防火墙和端口

HTTP-01 验证至少需要公网访问 TCP 80 端口,正式 HTTPS 访问需要 TCP 443 端口。

先确认服务器本地端口:

sudo ss -lntp | grep -E ':(80|443)\s'

如果使用 UFW,查看当前状态:

sudo ufw status verbose

只有在 UFW 已启用且规则缺失时,才添加规则:

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw status numbered

如果存在云平台安全组、机房防火墙或服务器面板防火墙,还需要在对应位置放行 80 和 443。修改防火墙前记录原有规则,避免把 SSH 管理端口一并锁死。

三、建立 ACME 验证目录和 HTTP 配置

1. 创建验证目录

创建专门用于证书验证的目录:

sudo install -d -o root -g root -m 755 /var/www/acme
sudo install -d -o root -g root -m 755 /var/www/acme/.well-known
sudo install -d -o root -g root -m 755 /var/www/acme/.well-known/acme-challenge

写入一个测试文件:

printf 'acme-ok\n' | sudo tee /var/www/acme/.well-known/acme-challenge/ping

2. 配置 HTTP 虚拟主机

创建配置文件:

sudo nano /etc/nginx/sites-available/multi-domain-http.conf

写入以下内容:

server {
    listen 80;
    listen [::]:80;

    server_name example.com www.example.com api.example.com;

    root /var/www/acme;

    location ^~ /.well-known/acme-challenge/ {
        default_type text/plain;
        try_files $uri =404;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

这段配置有两个作用:

  • /.well-known/acme-challenge/ 直接从验证目录提供文件;
  • 其他 HTTP 请求跳转到对应的 HTTPS 域名。

不能把所有 HTTP 请求直接跳转而不保留验证目录,否则某些验证请求可能无法取得挑战文件。

启用配置:

sudo ln -s /etc/nginx/sites-available/multi-domain-http.conf \
    /etc/nginx/sites-enabled/multi-domain-http.conf

如果提示目标文件已经存在,不要直接覆盖,先查看已有配置:

ls -l /etc/nginx/sites-enabled/
sudo nginx -T | grep -n -A8 -B3 'server_name example.com'

检查并加载配置:

sudo nginx -t && sudo systemctl reload nginx

只有 nginx -t 返回成功时,后面的 reload 才会执行。这样可以避免语法错误导致当前 Nginx 配置被错误替换。

3. 验证 HTTP 挑战路径

从服务器或外部网络访问:

curl -i http://example.com/.well-known/acme-challenge/ping
curl -i http://www.example.com/.well-known/acme-challenge/ping
curl -i http://api.example.com/.well-known/acme-challenge/ping

预期结果应包含:

HTTP/1.1 200 OK

响应正文应为:

acme-ok

如果返回 301,说明请求没有命中挑战目录;如果返回 404,说明 Nginx 的 root 路径、文件权限或虚拟主机匹配存在问题;如果连接超时,应优先检查 DNS、端口和防火墙。

四、申请多域名 SAN 证书

1. 先进行模拟验证

Certbot 支持使用测试环境执行模拟续期,可以先检查域名、验证目录和网络路径:

sudo certbot certonly \
  --webroot \
  -w /var/www/acme \
  -d example.com \
  -d www.example.com \
  -d api.example.com \
  --cert-name example.com \
  --dry-run

命令参数说明:

  • certonly:只申请证书,不自动修改 Nginx 配置;
  • --webroot:使用网站目录完成 HTTP 验证;
  • -w /var/www/acme:指定验证文件根目录;
  • 多个 -d:将多个域名加入同一张 SAN 证书;
  • --cert-name example.com:为证书配置指定稳定名称;
  • --dry-run:使用测试环境验证流程,不作为正式证书使用。

模拟验证成功后,再申请正式证书:

sudo certbot certonly \
  --webroot \
  -w /var/www/acme \
  -d example.com \
  -d www.example.com \
  -d api.example.com \
  --cert-name example.com \
  -m ops@example.com \
  --agree-tos \
  --no-eff-email \
  --non-interactive

ops@example.com 应替换为能够接收证书到期提醒的实际邮箱。示例域名不能直接用于生产申请,必须替换为已经由你控制并正确解析的域名。

2. 检查证书文件

Certbot 成功后,通常会生成以下路径:

/etc/letsencrypt/live/example.com/fullchain.pem
/etc/letsencrypt/live/example.com/privkey.pem

检查证书内容:

sudo certbot certificates
sudo openssl x509 \
  -in /etc/letsencrypt/live/example.com/fullchain.pem \
  -noout \
  -subject \
  -issuer \
  -dates \
  -ext subjectAltName

重点确认:

  • subjectAltName 中包含所有计划上线的域名;
  • notBefore 已经生效;
  • notAfter 尚未临近到期;
  • privkey.pem 与 fullchain.pem 位于同一个证书配置目录。

不要把 privkey.pem 设置为全员可读。Nginx 主进程通常以 root 启动并读取私钥,默认权限即可满足使用需求。

五、配置 HTTPS 虚拟主机

证书申请成功后,创建 HTTPS 配置:

sudo nano /etc/nginx/sites-available/multi-domain-https.conf

示例配置如下:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name example.com www.example.com api.example.com;

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

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    root /var/www/site;
    index index.html index.htm;

    location / {
        try_files $uri $uri/ /index.html;
    }
}

创建站点目录并放入测试页面:

sudo install -d -o root -g root -m 755 /var/www/site
printf '

HTTPS is working

\n' | sudo tee /var/www/site/index.html

启用配置:

sudo ln -s /etc/nginx/sites-available/multi-domain-https.conf \
    /etc/nginx/sites-enabled/multi-domain-https.conf

执行配置检查和优雅加载:

sudo nginx -t && sudo systemctl reload nginx

如果实际业务是运行在本机 8080 端口的应用,可以将 location / 替换为反向代理配置:

location / {
    proxy_pass http://127.0.0.1:8080;

    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

此处的 127.0.0.1:8080 只是示例,必须替换成实际应用监听地址。HTTPS 证书只负责客户端到 Nginx 的加密,后端进程是否正常仍需单独检查。

HSTS 的使用边界

确认所有相关域名都可以稳定使用 HTTPS 后,才考虑增加 HSTS:

add_header Strict-Transport-Security "max-age=31536000" always;

如果某些子域名仍然只支持 HTTP,不要贸然使用 includeSubDomains。HSTS 一旦被浏览器缓存,后续回退到 HTTP 会受到影响。

六、配置证书自动续期和零中断加载

1. 检查 Certbot 定时任务

Ubuntu 上的 Certbot 通常通过 systemd timer 定期执行续期检查:

systemctl list-timers --all | grep certbot
sudo systemctl status certbot.timer --no-pager

如果系统已经安装 certbot.timer 但没有运行,可以启用:

sudo systemctl enable --now certbot.timer

查看定时任务的完整配置:

sudo systemctl cat certbot.timer
sudo systemctl cat certbot.service

Certbot 的 renew 会根据证书剩余有效期判断是否需要真正续期,不是每次执行都会重新签发证书。

2. 创建部署钩子

创建 Certbot 部署钩子目录:

sudo install -d -m 755 /etc/letsencrypt/renewal-hooks/deploy

创建 Nginx 平滑加载脚本:

sudo nano /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

写入:

#!/bin/sh
set -eu

/usr/sbin/nginx -t
/usr/bin/systemctl reload nginx

设置权限:

sudo chown root:root /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
sudo chmod 750 /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

确认命令路径与当前系统一致:

command -v nginx
command -v systemctl

如果路径不同,应按实际输出修改脚本。

3. 为什么使用 reload 而不是 restart

不应将续期后的动作写成:

sudo systemctl restart nginx

restart 会停止并重新启动服务,可能造成短暂连接失败。正确流程是:

左侧显示部署钩子命令 /usr/sbin/nginx -t 与 /usr/bin/systemctl reload nginx,下面显示示意性的 syntax

sudo nginx -t && sudo systemctl reload nginx

Nginx reload 的特点是:

  • 先检查并加载新配置;
  • 新 worker 使用新证书接受连接;
  • 旧 worker 继续处理已有连接;
  • 旧 worker 完成连接后退出;
  • 不主动中断正常请求。

证书文件更新后,Nginx 不会自动重新读取私钥,因此部署钩子中的 reload 是必要步骤。Certbot 更新的是 /etc/letsencrypt/live/ 下的符号链接,Nginx 配置应固定引用 live 路径,而不是直接引用 archive 中的版本文件。

4. 验证自动续期流程

先执行测试续期:

sudo certbot renew --dry-run

如果当前 Certbot 支持执行测试部署钩子,可以运行:

sudo certbot renew --dry-run --run-deploy-hooks

如果命令提示不支持该参数,先查看帮助:

certbot renew --help

也可以直接手动执行部署钩子,验证脚本和 reload 流程:

sudo /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
sudo systemctl is-active nginx

查看相关日志:

sudo journalctl -u certbot --since "24 hours ago" --no-pager
sudo journalctl -u nginx --since "24 hours ago" --no-pager

测试时如果 nginx -t 失败,脚本会停止执行 reload。此时旧的 Nginx worker 通常仍在提供服务,先修复配置,不要强行重启。

七、逐域名验证 HTTPS 结果

1. 使用 curl 检查 HTTP 跳转

curl -I http://example.com
curl -I http://www.example.com
curl -I http://api.example.com

正常情况下应看到类似结果:

HTTP/1.1 301 Moved Permanently
Location: https://example.com/

Location 中应保留原始域名和请求路径,不应把所有域名错误跳转到其中一个域名。

2. 使用 curl 检查 HTTPS

curl -I https://example.com
curl -I https://www.example.com
curl -I https://api.example.com

静态站点一般会返回 200 OK。如果业务本身使用鉴权,也可能返回 401 或 403,这不一定代表 TLS 失败,只要证书验证成功且请求已经到达业务层即可。

3. 使用 OpenSSL 检查 SNI 和证书域名

多域名 HTTPS 依赖 SNI,验证时必须带上 -servername:

for domain in example.com www.example.com api.example.com; do
    echo "===== $domain ====="
    echo | openssl s_client \
        -connect "$domain:443" \
        -servername "$domain" \
        2>/dev/null \
        | openssl x509 -noout -subject -issuer -dates -ext subjectAltName
done

重点检查:

  • 每个域名都能建立 TLS 连接;
  • 证书的 SAN 包含当前域名;
  • notAfter 距离当前时间仍有足够余量;
  • 不同域名没有被错误地返回默认虚拟主机证书。

4. 检查 Nginx 是否引用正确证书

sudo readlink -f /etc/letsencrypt/live/example.com/fullchain.pem
sudo readlink -f /etc/letsencrypt/live/example.com/privkey.pem
sudo nginx -T | grep -E 'ssl_certificate(_key)?'

如果浏览器显示旧证书,重点检查三项:

  1. Nginx 是否引用了 live 路径;
  2. Certbot 是否更新了符号链接;
  3. 续期后是否执行过 reload。

可以使用以下命令确认 Nginx 已加载的公开证书:

echo | openssl s_client \
  -connect example.com:443 \
  -servername example.com \
  2>/dev/null \
  | openssl x509 -noout -dates -fingerprint -sha256

八、常见失败情况与处理顺序

1. 域名解析错误

典型现象:

NXDOMAIN
SERVFAIL
Connection timed out

处理顺序:

  1. 使用 dig +short A 域名 检查解析;
  2. 确认结果是当前香港服务器公网地址;
  3. 检查是否存在指向旧服务器的 AAAA 记录;
  4. 确认 DNS 变更已经在各递归 DNS 中生效;
  5. 再次访问 HTTP 验证文件。

域名没有指向当前服务器时,继续修改 Nginx 不会解决问题。

2. ACME 验证返回 404

先模拟验证文件:

sudo ls -l /var/www/acme/.well-known/acme-challenge/ping
curl -i http://example.com/.well-known/acme-challenge/ping

重点检查:

  • server_name 是否包含正在申请的域名;
  • root 是否为 /var/www/acme;
  • 文件是否位于 .well-known/acme-challenge/;
  • 是否有其他默认虚拟主机抢先匹配;
  • Nginx 是否已经 reload 最新配置。

如果 HTTP 请求被跳转到其他域名,说明虚拟主机或跳转配置需要调整。

3. ACME 验证超时

验证超时通常与网络路径有关:

八、常见失败情况与处理顺序 / 3. ACME 验证超时配图

  • 香港服务器的 80 端口没有监听;
  • 本机防火墙未放行;
  • 云平台安全组未放行;
  • 上游防火墙拦截了入站连接;
  • AAAA 记录指向无法访问的 IPv6 地址。

可以从外部网络执行:

curl -v http://example.com/.well-known/acme-challenge/ping

不要只在服务器本机访问 127.0.0.1,本机成功不能证明公网验证一定成功。

4. nginx -t 检查失败

常见错误包括:

  • 证书文件路径不存在;
  • 私钥和证书路径写错;
  • 虚拟主机括号缺失;
  • 443 端口重复绑定;
  • IPv6 监听地址不可用;
  • 配置文件重复定义或引用了不存在的 include 文件。

查看完整错误:

sudo nginx -t

在配置检查通过前,不要执行 reload。由于部署脚本使用了 nginx -t,证书续期后即使配置检查失败,也不会主动加载错误配置。

5. HTTPS 返回 502

如果 TLS 验证成功但页面返回 502 Bad Gateway,问题通常不在证书,而在反向代理后端:

sudo ss -lntp | grep ':8080'
curl -i http://127.0.0.1:8080
sudo journalctl -u nginx --since "30 minutes ago" --no-pager

如果后端实际没有监听 8080,需要修改 proxy_pass;如果后端只监听 Unix Socket,也应按实际路径配置。

6. 续期成功但浏览器仍显示旧证书

检查 Certbot 续期记录:

sudo certbot certificates
sudo journalctl -u certbot --since "7 days ago" --no-pager

检查部署钩子:

sudo /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

再检查线上证书:

echo | openssl s_client \
  -connect example.com:443 \
  -servername example.com \
  2>/dev/null \
  | openssl x509 -noout -dates -fingerprint -sha256

如果 Certbot 已更新文件,但线上指纹没有变化,通常是 Nginx 未 reload,或者配置仍然引用旧的证书文件路径。

九、配置失败时的回滚方法

1. Nginx 配置回滚

如果新配置尚未执行 reload,当前运行中的 Nginx 通常仍使用旧配置。先不要执行 restart,直接恢复备份:

sudo nginx -t

如果检查失败,将当前配置目录先改名,再恢复备份目录。以下路径需要替换为实际备份目录:

sudo mv /etc/nginx /root/nginx-backup/nginx-failed-$(date +%Y%m%d-%H%M%S)
sudo cp -a /root/nginx-backup/nginx-before-YYYYMMDD-HHMMSS /etc/nginx
sudo nginx -t && sudo systemctl reload nginx

该操作会替换整个 Nginx 配置目录,只能在确认备份完整、并且需要恢复整套配置时使用。若只是单个站点配置错误,优先恢复对应文件,避免影响其他站点。

2. 证书配置回滚

如果只是证书续期钩子执行失败,不要删除证书,也不要立即撤销证书。旧证书仍可能被当前 Nginx worker 使用,优先修复:

  • 证书文件路径;
  • 私钥权限;
  • Nginx 配置语法;
  • reload 命令路径;
  • Certbot hook 文件权限。

如果确实需要恢复旧证书,应使用之前受保护的证书备份,并先将 Nginx 配置指向备份后的证书文件,再执行:

sudo nginx -t && sudo systemctl reload nginx

不要直接手工修改 /etc/letsencrypt/live/ 下的符号链接,也不要随意删除 archive 和 renewal 目录,否则可能破坏后续自动续期。

3. 自动续期任务的回滚边界

不要为了处理一次续期失败而长期关闭 certbot.timer。正确做法是:

  1. 确认证书当前剩余有效期;
  2. 修复 HTTP 验证或 Nginx reload 问题;
  3. 执行 certbot renew --dry-run;
  4. 确认部署钩子可以正常完成;
  5. 保持定时任务运行。

只有在进行短时间维护且已有明确的人工续期方案时,才考虑临时停止定时任务,并在维护完成后立即恢复。

十、上线验收与回滚检查项

上线验收

  • [ ] 所有域名的 A/AAAA 记录指向正确服务器。
  • [ ] 公网 TCP 80 和 443 端口已放行。
  • [ ] /.well-known/acme-challenge/ 可以返回测试文件。
  • [ ] 正式证书 SAN 包含所有目标域名。
  • [ ] sudo nginx -t 返回成功。
  • [ ] HTTP 请求按预期跳转到 HTTPS。
  • [ ] 每个域名都能通过 SNI 返回匹配证书。
  • [ ] certbot.timer 处于运行或可调度状态。
  • [ ] /etc/letsencrypt/renewal-hooks/deploy/ 中的脚本可执行。
  • [ ] certbot renew --dry-run 验证成功。
  • [ ] 续期后使用 reload,没有配置 restart。
  • [ ] 后端应用返回状态正常,代理域名没有 502。

回滚验收

  • [ ] Nginx 原始配置备份可读取。
  • [ ] /etc/letsencrypt 备份权限仅限管理员。
  • [ ] 恢复配置后 nginx -t 通过。
  • [ ] 回滚操作使用 systemctl reload nginx,没有盲目重启。
  • [ ] 回滚后每个域名仍能建立 HTTPS 连接。
  • [ ] 没有删除 live、archive 或 renewal 目录。
  • [ ] 自动续期任务没有被无期限停用。
  • [ ] 修复问题后重新执行模拟续期和部署钩子测试。

通过以上方式,香港服务器上的多个域名可以共用一套可维护的 HTTPS 部署流程:初次申请使用 Webroot 验证,Nginx 固定引用 Certbot 的 live 证书路径,续期后先检查配置再平滑 reload,出现错误时保留旧进程和旧证书服务,并通过备份配置完成回滚。

目录结构
全文