跳到主要内容

使用 acme.sh 为 Nginx UI 管理的站点配置 HTTPS

本指南使用 acme.sh 的 DNS-01 模式签发证书,把证书链和私钥安装到稳定的 Nginx 路径, 再由 Nginx UI 管理站点配置。完成后,证书能够自动续期,并在 nginx -t 通过后重载 Nginx。

本文适合公网 80 端口不可用、需要通配符证书,或希望把“签发证书”和“Nginx UI 管理站点” 分开排查的场景。如果 HTTP-01 链路简单且公网 80 端口可达,使用 Nginx UI 内置申请功能也 是有效选择。

前置条件

  • 一台运行 Nginx 和 Nginx UI 的 Linux 主机;
  • 域名已经解析到目标服务,且你能修改其权威 DNS;
  • DNS 提供商支持 API,例如阿里云或 DNSPod;
  • API 凭据只能修改所需 DNS 区域,避免使用账号级全权限密钥;
  • 主机可以出站访问 ACME CA 和 DNS API;
  • 已备份当前 Nginx 配置和证书目录。

本文使用以下占位值:

占位符含义
app.example.com申请证书的站点域名
/etc/nginx/ssl/app.example.com稳定的生产证书目录
<DNS_API_KEY>DNS API AccessKey 或 ID
<DNS_API_SECRET>DNS API Secret 或 Token

不要把真实 API 密钥写入 shell 历史、Nginx 配置、Git 仓库或问题截图。

1. 先确认基础服务和域名

检查 Nginx UI、Nginx 配置以及 80/443 端口:

systemctl status nginx-ui --no-pager
journalctl -u nginx-ui -n 100 --no-pager
nginx -t
systemctl status nginx --no-pager
ss -lntp | rg ':(80|443)\b'

再确认站点解析和权威 DNS:

dig A app.example.com +short
dig AAAA app.example.com +short
dig NS example.com +short

AAAA 记录存在时,必须确认 IPv6 也能到达正确主机;否则 ACME 验证和浏览器访问可能出现 IPv4 成功、IPv6 失败的分裂现象。DNS-01 不要求站点开放 80 端口,但 DNS API 必须操作 dig NS 返回的权威 DNS 区域。

2. 理解 Nginx UI 直接申请证书的边界

Nginx UI 支持 ACME 证书申请和续期,但应注意:

  • HTTPChallengePort 是 Nginx UI 后端的 HTTP-01 挑战监听端口,文档默认值为 9180
  • Let’s Encrypt 的 HTTP-01 验证仍只从公网 80 端口发起,请求必须被正确转发到挑战处理程序;
  • DNS-01 使用系统 DNS,或 Nginx UI RecursiveNameservers 中配置的递归服务器;
  • 自动发现只识别分离的证书和私钥文件,例如 fullchain.pemprivkey.pem
  • 合并了证书和私钥的单个 PEM 文件不会被自动发现。

因此,Nginx UI 中申请失败时,不要连续切换 CA、挑战方式和 DNS 凭据。先从日志确认失败发生 在服务启动、订单创建、域名验证、文件保存还是 Nginx 重载阶段。

3. 安装 acme.sh

先阅读安装脚本,再决定是否执行。官方安装命令会把程序放到 ~/.acme.sh/,创建 shell 别名,并添加定时任务:

curl -fsSL https://get.acme.sh -o /tmp/get-acme.sh
less /tmp/get-acme.sh
sh /tmp/get-acme.sh email=<ACME_ACCOUNT_EMAIL>

重新打开 shell,或直接使用完整路径检查版本和帮助:

~/.acme.sh/acme.sh --version
~/.acme.sh/acme.sh --help

4. 配置 DNS API 并签发证书

以下示例以阿里云 DNS 为例。变量名和 dns_ali 必须与 acme.sh 的 DNS API 实现一致:

export Ali_Key='<DNS_API_KEY>'
export Ali_Secret='<DNS_API_SECRET>'

~/.acme.sh/acme.sh --issue \
--server letsencrypt \
--dns dns_ali \
-d app.example.com

如果权威 DNS 实际由 DNSPod 管理,应改用 DNSPod 对应的 dns_dp 和官方文档列出的凭据 变量;不要把阿里云凭据用于 DNSPod 区域,反之亦然。不同提供商的密钥格式和权限模型不可 互换。

需要同时覆盖根域和通配符域名时:

~/.acme.sh/acme.sh --issue \
--server letsencrypt \
--dns dns_ali \
-d example.com \
-d '*.example.com'

acme.sh 当前默认签发 ECC 证书。后续安装和查询命令应保留 --ecc。不要频繁使用 --force 重试生产 CA;排障时先检查日志、API 权限和 TXT 传播,必要时使用 CA 的 staging 环境,避免触发签发频率限制。

当验证卡在 DNS 时,从多个递归服务器查询:

dig TXT _acme-challenge.app.example.com +short
dig @1.1.1.1 TXT _acme-challenge.app.example.com +short
dig @8.8.8.8 TXT _acme-challenge.app.example.com +short

DNS-01 适合自动续期的前提是使用 DNS API。手工添加 TXT 的模式需要每次续期重新操作, 不应当被当成无人值守方案。

5. 安装证书到稳定路径

不要让 Nginx 直接引用 ~/.acme.sh/ 中的内部文件。先创建目标目录,并限制私钥目录权限:

sudo install -d -m 700 /etc/nginx/ssl/app.example.com

使用 --install-cert 复制证书链和私钥,并配置安全的重载命令:

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

确认文件权限。私钥只应对需要读取它的特权进程开放:

sudo ls -l /etc/nginx/ssl/app.example.com

6. 在 Nginx UI 中配置站点

在 Nginx UI 中编辑目标站点,让 TLS 配置分别引用:

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

保存前先使用 Nginx UI 的配置测试;也可以在服务器执行:

sudo nginx -t

只有测试通过后才重载:

sudo systemctl reload nginx

若希望使用 Nginx UI 的证书自动发现,确认它配置的 Nginx ssl 目录覆盖上述路径,并保留 证书链与私钥两个独立文件。自动发现不是启用 TLS 的必要条件;站点明确引用正确路径即可。

7. 验证证书、私钥和线上服务

查看证书主题、签发者和有效期:

sudo openssl x509 \
-in /etc/nginx/ssl/app.example.com/fullchain.pem \
-noout -subject -issuer -dates

比较证书与私钥的公钥摘要,两条输出必须相同:

sudo openssl x509 \
-in /etc/nginx/ssl/app.example.com/fullchain.pem \
-pubkey -noout | openssl sha256
sudo openssl pkey \
-in /etc/nginx/ssl/app.example.com/privkey.pem \
-pubout | openssl sha256

从客户端使用 SNI 检查线上实际返回的证书链:

openssl s_client \
-connect app.example.com:443 \
-servername app.example.com \
-showcerts </dev/null

如果磁盘证书正确但线上仍展示旧证书,重点检查:

  • 请求是否到达另一台代理、CDN 或 IPv6 主机;
  • Nginx 站点是否引用了另一个证书路径;
  • nginx -t 和 reload 是否真正成功;
  • 443 端口是否由其他进程监听。

8. 验证自动续期

查看证书记录和下一次续期时间:

~/.acme.sh/acme.sh --info -d app.example.com --ecc

运行一次正常的 cron 检查;它不会无条件强制签发:

~/.acme.sh/acme.sh --cron --home ~/.acme.sh

再检查定时任务、证书时间和 Nginx 日志:

crontab -l
sudo openssl x509 \
-in /etc/nginx/ssl/app.example.com/fullchain.pem \
-noout -dates
sudo journalctl -u nginx -n 50 --no-pager

不要为了“测试续期”反复对生产 CA 使用 --renew --force。若确实需要端到端演练,应先备份 证书目录,并在 ACME staging 环境完成。

回滚

改动站点前,先备份当前配置和证书目录:

sudo cp -a /etc/nginx/nginx.conf /etc/nginx/nginx.conf.before-https
sudo cp -a /etc/nginx/ssl /etc/nginx/ssl.before-https

如果新配置导致 Nginx 测试失败,恢复原站点配置或原证书路径,然后执行:

sudo nginx -t && sudo systemctl reload nginx

不要在未确认其他站点依赖关系时删除整个 ssl 目录,也不要用空文件覆盖私钥。

参考资料

评论