nginx-acme模块无法申请证书解决方案
文章目录
问题描述
为了省略每次手动申请SSL证书的麻烦,自己手工编译了nginx,并使用了nginx-acme模块来自动申请和续订证书。但是在实际使用过程中,发现无法成功申请证书。
使用了官方的文档进行配置:
nginx的http模块增加
# 配置 DNS 解析器(用于 ACME 客户端与 CA 服务器通信)
resolver 127.0.0.1:53;
# 定义一个名为 “au92” 的 ACME 颁发者配置
acme_issuer au92 {
# 替换为实际的 ACME 目录服务地址,例如 Let‘s Encrypt 是 https://acme-v02.api.letsencrypt.org/directory
uri https://acme-v02.api.letsencrypt.org/directory; # **需要替换:改为你的 ACME CA 地址**
# 可选:联系邮箱
contact iam@au92.com; # **需要替换:改为你的邮箱(可选)**
# 指定状态文件存储路径
state_path /var/cache/nginx/acme-au92;
# 接受 CA 的服务条款
accept_terms_of_service;
}
# 可选:共享内存区域,用于工作进程间同步 ACME 数据
acme_shared_zone zone=ngx_acme_shared:1M;
server模块增加
server {
listen 80;
server_name history.au92.com;
# return 301 https://history.au92.com$request_uri;
# 一个安全实践:将其他所有 80 端口的非验证请求返回 404 或重定向到 HTTPS
# ACME 模块会自动处理 /.well-known/acme-challenge/ 的请求,此 location 用于处理所有其他请求
location / {
# 将所有非 ACME 验证的 HTTP 流量强制重定向到 HTTPS
return 301 https://$host$request_uri;
}
}
server {
access_log /home/logs/nginx/history.au92.com.log main if=$loggable;
# HTTP/1.1 + TLS
listen 443 ssl;
# QUIC (HTTP/3)
listen 443 quic reuseport;
# 推荐的 HTTP/2 现代写法
http2 on;
server_name history.au92.com;
index index.html index.htm;
# 使用 nginx-acme 模块提供的变量指定证书和密钥路径
acme_certificate au92;
ssl_certificate $acme_certificate;
ssl_certificate_key $acme_certificate_key;
ssl_certificate_cache max=2;
ssl_protocols TLSv1.3;
ssl_prefer_server_ciphers off;
add_header Alt-Svc 'h3=":443"; ma=86400';
}
但是实际运行后,发现无法成功申请证书。
现状
/var/cache/nginx/acme-au92目录下只有一个account.key文件,没有任何证书文件。
排查过程
经过一圈Google后,找到一篇文章NGINX 原生 ACME 支持:从根本上重塑 TLS 自动化部署,文章中的实例是把resolver指向了resolver 8.8.8.8 1.1.1.1;,于是尝试修改了resolver的配置,但是依然无法成功申请证书。
查看nginx的错误日志发现:[error] 325871#325871: connect() to acme-v02.api.letsencrypt.org failed (101: Network is unreachable)
但是此时ping acme-v02.api.letsencrypt.org是可以通的,说明网络没有问题。
请教了AI(Gemini),它建议我修改DNS为只解析IPV4resolver 8.8.8.8 1.1.1.1 ipv6=off;
Ping 成功: 你运行 ping acme-v02.api.letsencrypt.org 时,输出显示连接的是 172.65.32.248(IPv4 地址)。这说明你的服务器 IPv4 网络是正常的。
Nginx 失败: Nginx 使用你配置的 resolver 8.8.8.8 解析域名。Let's Encrypt 同时支持 IPv4 和 IPv6。如果 8.8.8.8 返回了 IPv6 地址(AAAA 记录),Nginx 会尝试使用 IPv6 连接。
错误 101: Network is unreachable 通常意味着 Nginx 拿到了 IPv6 地址,尝试建立连接,但你的服务器 不支持 IPv6 或者 IPv6 路由配置错误(很多 VPS 默认开启 IPv6 接口但没有配置实际的公网 IPv6 路由)。
修改之后发现依然申请不到证书,查看日志发现没有任何输出,怀疑是日志级别不够,于是把nginx的error_log级别调成了infoerror_log /var/log/nginx/error.log info; (失误的是AI第一次告诉我把这一行加在http模块内),经过几轮往复最后依然是Gemini告诉我需要加载http模块外边。
最终终于拿到了日志:
upstream SSL certificate verify error: (20:"unable to get local issuer certificate")
最终解决方案
apt-get update
apt-get install -y ca-certificates
然后确认ls -lh /etc/ssl/certs/ca-certificates.crt文件存在
修改nginx.service,增加一行:Environment="SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt"
vi /etc/systemd/system/nginx.service
[Unit]
Description=nginx - high performance web server
Documentation=https://nginx.org/en/docs/
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target
[Service]
Type=forking
PIDFile=/run/nginx.pid
Environment="CONFFILE=/etc/nginx/nginx.conf"
ExecStart=/usr/share/nginx/sbin/nginx -c ${CONFFILE}
ExecReload=/usr/share/nginx/sbin/nginx -s reload
ExecStop=/usr/share/nginx/sbin/nginx -s quit
Environment="SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt"
[Install]
WantedBy=multi-user.target
systemctl enable nginx
nginx.conf的http模块内增加:
# 配置 DNS 解析器(用于 ACME 客户端与 CA 服务器通信)
resolver 8.8.8.8 1.1.1.1 ipv6=off;
# 定义一个名为 “au92” 的 ACME 颁发者配置
acme_issuer au92 {
# 替换为实际的 ACME 目录服务地址,例如 Let‘s Encrypt 是 https://acme-v02.api.letsencrypt.org/directory
uri https://acme-v02.api.letsencrypt.org/directory; # **需要替换:改为你的 ACME CA 地址**
# 可选:联系邮箱
contact iam@au92.com; # **需要替换:改为你的邮箱(可选)**
# 指定状态文件存储路径
state_path /var/cache/nginx/acme-au92;
# 接受 CA 的服务条款
accept_terms_of_service;
}
# 可选:共享内存区域,用于工作进程间同步 ACME 数据
acme_shared_zone zone=ngx_acme_shared:1M;
server模块内增加(注意80的server模块要一定保留):
# 使用 nginx-acme 模块提供的变量指定证书和密钥路径
acme_certificate au92;
ssl_certificate $acme_certificate;
ssl_certificate_key $acme_certificate_key;
ssl_certificate_cache max=2;
原因:
我是手动编译的 OpenSSL 3.6.0。系统自带的 OpenSSL 知道 Debian 的根证书存放位置(通常在 /etc/ssl/certs),但你手动编译的 OpenSSL 默认会在它自己的安装目录(例如 /usr/local/ssl)里找证书,那里是空的。因此,它认为 Let's Encrypt 的证书是“伪造”的,拒绝连接。
文章作者 pengxiaochao
上次更新 2025-12-03
许可协议 不允许任何形式转载。