香港服务器多域名HTTPS如何部署SSL证书全自动续期并实现零中断
在香港服务器上实现多域名 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. 证书续期的工作链路
完整链路如下:

- 域名 A、域名 B、域名 C 解析到香港服务器。
- Certbot 在
/var/www/acme/.well-known/acme-challenge/写入验证文件。 - 证书服务通过 HTTP 访问每个域名的验证地址。
- 证书续期成功后,Certbot 更新
/etc/letsencrypt/live/下的符号链接。 - 部署钩子执行
nginx -t。 - 配置检查通过后执行
systemctl reload nginx。 - 新建 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 会停止并重新启动服务,可能造成短暂连接失败。正确流程是:

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)?'
如果浏览器显示旧证书,重点检查三项:
- Nginx 是否引用了
live路径; - Certbot 是否更新了符号链接;
- 续期后是否执行过 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
处理顺序:
- 使用
dig +short A 域名检查解析; - 确认结果是当前香港服务器公网地址;
- 检查是否存在指向旧服务器的 AAAA 记录;
- 确认 DNS 变更已经在各递归 DNS 中生效;
- 再次访问 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 验证超时
验证超时通常与网络路径有关:

- 香港服务器的 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。正确做法是:
- 确认证书当前剩余有效期;
- 修复 HTTP 验证或 Nginx reload 问题;
- 执行
certbot renew --dry-run; - 确认部署钩子可以正常完成;
- 保持定时任务运行。
只有在进行短时间维护且已有明确的人工续期方案时,才考虑临时停止定时任务,并在维护完成后立即恢复。
十、上线验收与回滚检查项
上线验收
- [ ] 所有域名的 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,出现错误时保留旧进程和旧证书服务,并通过备份配置完成回滚。