NGINX中通过$host动态指定SSL证书路径失败的问题排查
单Nginx Server块动态加载多域名SSL证书失败问题排查
问题描述
我拥有两个SSL证书,分别对应域名foo.com和bar.com。我希望通过单个server配置块同时为这两个域名提供服务,配置如下:
server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name foo.com bar.com; ssl_certificate $cert_path/fullchain.pem; ssl_certificate_key $cert_path/privkey.pem; }
在http { .. }块中,我通过map指令定义了$cert_path变量:
map $host $cert_path { foo.com /etc/certbot/live/foo.org; bar.com /etc/certbot/live/bar.org; }
但我持续收到如下日志错误:
2024/10/04 11:30:13 [error] 9#9: *73 cannot load certificate "/fullchain.pem": BIO_new_file() failed (SSL: error:80000002:system library::No such file or directory:calling fopen(/fullchain.pem, r) error:10000080:BIO routines::no such file) while SSL handshaking, client: x.x.x.x, server: 0.0.0.0:443
这种通过map结合$host动态加载证书的方式似乎无法正常工作,请问可能是什么原因导致的?
原因分析与解决办法
核心问题
- 变量时机错误:Nginx在SSL握手阶段无法获取
$host变量——$host依赖HTTP请求头中的Host字段,而SSL握手发生在HTTP请求之前。这导致$cert_path变量在加载证书时为空,Nginx只能尝试加载/fullchain.pem(缺少前缀路径),最终报错文件不存在。 - 路径笔误:map配置中,
foo.com对应的证书路径写为/etc/certbot/live/foo.org,bar.com对应/etc/certbot/live/bar.org,域名后缀与实际证书目录不一致,即使变量正常工作,也会找不到正确的证书文件。
正确配置方案
要实现单server块动态加载多域名证书,需使用$ssl_server_name变量(该变量在SSL握手阶段即可通过SNI扩展获取),并确保Nginx版本≥1.15.9(此版本开始支持用变量动态指定SSL证书路径):
- 修正map指令,替换变量并修正路径:
map $ssl_server_name $cert_path { foo.com /etc/certbot/live/foo.com; bar.com /etc/certbot/live/bar.com; }
- 保留server块配置,可显式声明SNI支持(默认已开启):
server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name foo.com bar.com; ssl_certificate $cert_path/fullchain.pem; ssl_certificate_key $cert_path/privkey.pem; ssl_prefer_server_ciphers on; }
额外注意事项
- 确保Nginx进程用户(如www-data、nginx)拥有证书目录及文件的读取权限,Certbot生成的证书目录默认权限通常需要调整为755,文件权限为644。
- 验证Certbot生成的符号链接是否有效:
/etc/certbot/live/域名/下的fullchain.pem和privkey.pem是指向真实证书文件的符号链接,需确保链接未失效。
内容的提问来源于stack exchange,提问作者Jeanluca Scaljeri
相关产品推荐
相关产品推荐

