如何配置Nginx反向代理外网HTTPS服务解决SNI域名缺失导致的502错误问题?
- 内容介绍
- 文章标签
- 相关推荐
本文共计850个文字,预计阅读时间需要4分钟。
直接输出结论:
怎么确认是 SNI 缺失引起的 502
别猜,看日志。打开 /var/log/nginx/error.log,重点搜这几类线索:
-
SSL_do_handshake() failed或SSL_connect() failed -
tlsv1 alert handshake failure(alert number 40)或tlsv1 alert internal error(alert number 80) - 日志里显示 Nginx 把
api.example.com解析成192.0.2.1,但你用curl -v https://192.0.2.1确实失败,而curl -v https://api.example.com成功
满足以上任意一条,基本就是 SNI 问题——后端 HTTPS 服务(比如 Cloudflare、某些 SaaS API)靠 SNI 区分虚拟主机,Nginx 不带 SNI,它就拒连。
proxy_ssl_server_name on 必须配合 proxy_ssl_name
只写 proxy_ssl_server_name on 不够,Nginx 还不知道该发哪个域名过去。它不会自动从 proxy_pass https://xxx 或 upstream 里提取,必须显式指定:
- 如果
proxy_pass直写 URL:proxy_pass https://api.example.com;
则 location 块内必须加:proxy_ssl_server_name on;proxy_ssl_name "api.example.com"; - 如果用了
upstream:upstream backend { server api.example.com:443; }
同样要在 location 里写全两行,且proxy_ssl_name的值必须和 upstream 中 server 后的域名一致(不是 IP) -
proxy_ssl_name值必须是字符串,带引号;大小写不敏感,但建议和证书 CN 或 SAN 完全一致
为什么加了 SNI 还报 404 或证书错误
SNI 只解决握手第一步,后面还有三处常被忽略:
-
proxy_set_header Host必须设成目标域名,不能留$host:
后端服务靠这个路由请求,SNI 是 TLS 层,Host 是 HTTP 层,两者独立。
错例:proxy_set_header Host $host;→ 改为proxy_set_header Host "api.example.com"; - 证书验证默认开启,若后端用自签/通配符/域名不匹配证书,会卡在握手后证书校验阶段:
测试环境可临时关:proxy_ssl_verify off;
生产环境必须配proxy_ssl_trusted_certificate指向可信 CA 证书链 - HTTPS 往往更慢,超时要调大:
proxy_connect_timeout 15s;proxy_send_timeout 60s;proxy_read_timeout 60s;(尤其含大文件上传或长轮询时)
容易踩的坑:upstream 写 IP 就废了
upstream 里写 server 192.0.2.1:443; 是无效的——proxy_ssl_server_name on 在 IP 场景下不生效,Nginx 无法构造 SNI 扩展。必须写域名:
- 正确:
upstream backend { server api.example.com:443; } - 错误:
upstream backend { server 192.0.2.1:443; }(哪怕 DNS 解析结果一样也不行) - DNS 解析由 Nginx 主动完成,不是系统 resolver;如果域名解析不稳定,可加
resolver 8.8.8.8 valid=30s;到 http 块
真正麻烦的是多租户场景:同一个 IP 托管多个 HTTPS 域名,仅靠 SNI + Host 头还不够,可能还需 proxy_ssl_name 动态化(用变量),但这要求 Nginx ≥ 1.19.0 且配置更谨慎——多数人卡在这一步,而不是最初那两行。
本文共计850个文字,预计阅读时间需要4分钟。
直接输出结论:
怎么确认是 SNI 缺失引起的 502
别猜,看日志。打开 /var/log/nginx/error.log,重点搜这几类线索:
-
SSL_do_handshake() failed或SSL_connect() failed -
tlsv1 alert handshake failure(alert number 40)或tlsv1 alert internal error(alert number 80) - 日志里显示 Nginx 把
api.example.com解析成192.0.2.1,但你用curl -v https://192.0.2.1确实失败,而curl -v https://api.example.com成功
满足以上任意一条,基本就是 SNI 问题——后端 HTTPS 服务(比如 Cloudflare、某些 SaaS API)靠 SNI 区分虚拟主机,Nginx 不带 SNI,它就拒连。
proxy_ssl_server_name on 必须配合 proxy_ssl_name
只写 proxy_ssl_server_name on 不够,Nginx 还不知道该发哪个域名过去。它不会自动从 proxy_pass https://xxx 或 upstream 里提取,必须显式指定:
- 如果
proxy_pass直写 URL:proxy_pass https://api.example.com;
则 location 块内必须加:proxy_ssl_server_name on;proxy_ssl_name "api.example.com"; - 如果用了
upstream:upstream backend { server api.example.com:443; }
同样要在 location 里写全两行,且proxy_ssl_name的值必须和 upstream 中 server 后的域名一致(不是 IP) -
proxy_ssl_name值必须是字符串,带引号;大小写不敏感,但建议和证书 CN 或 SAN 完全一致
为什么加了 SNI 还报 404 或证书错误
SNI 只解决握手第一步,后面还有三处常被忽略:
-
proxy_set_header Host必须设成目标域名,不能留$host:
后端服务靠这个路由请求,SNI 是 TLS 层,Host 是 HTTP 层,两者独立。
错例:proxy_set_header Host $host;→ 改为proxy_set_header Host "api.example.com"; - 证书验证默认开启,若后端用自签/通配符/域名不匹配证书,会卡在握手后证书校验阶段:
测试环境可临时关:proxy_ssl_verify off;
生产环境必须配proxy_ssl_trusted_certificate指向可信 CA 证书链 - HTTPS 往往更慢,超时要调大:
proxy_connect_timeout 15s;proxy_send_timeout 60s;proxy_read_timeout 60s;(尤其含大文件上传或长轮询时)
容易踩的坑:upstream 写 IP 就废了
upstream 里写 server 192.0.2.1:443; 是无效的——proxy_ssl_server_name on 在 IP 场景下不生效,Nginx 无法构造 SNI 扩展。必须写域名:
- 正确:
upstream backend { server api.example.com:443; } - 错误:
upstream backend { server 192.0.2.1:443; }(哪怕 DNS 解析结果一样也不行) - DNS 解析由 Nginx 主动完成,不是系统 resolver;如果域名解析不稳定,可加
resolver 8.8.8.8 valid=30s;到 http 块
真正麻烦的是多租户场景:同一个 IP 托管多个 HTTPS 域名,仅靠 SNI + Host 头还不够,可能还需 proxy_ssl_name 动态化(用变量),但这要求 Nginx ≥ 1.19.0 且配置更谨慎——多数人卡在这一步,而不是最初那两行。

