nginx 代理 wss 时需启用 proxy_ssl_server_name on 并设 proxy_ssl_name $host(或 $http_x_forwarded_host),确保 tls 握手发送真实请求域名以匹配证书 san,否则导致 400/502 或断连;proxy_pass 必须为 https,upstream 场景下 proxy_ssl_name 需用带引号的字面量。

代理到外部 WSS(WebSocket Secure)服务时,Nginx 默认不发送 SNI 信息,导致后端 TLS 握手失败、证书校验不通过或连接被拒绝。关键不是只开 proxy_ssl_server_name on,而是确保它把客户端请求的真实域名作为 SNI 值发给 WSS 服务端——否则对方无法匹配证书中的 SAN,返回 400、502 或直接断连。
必须启用 SNI 并绑定真实请求域名
WSS 基于 HTTPS 升级(Upgrade: websocket),其 TLS 握手与普通 HTTPS 完全一致,因此同样依赖 SNI 匹配证书。Nginx 默认禁用该扩展,需显式开启并指定值:
- 在
location块中添加proxy_ssl_server_name on; - 紧接着配置
proxy_ssl_name $host;(推荐)或proxy_ssl_name $http_host;,确保 SNI 域名与用户访问的 WSS 地址一致(如wss://app.example.com/ws→ SNI 发app.example.com) -
proxy_pass必须为 HTTPS 地址,例如proxy_pass https://wss-backend;或proxy_pass https://192.168.10.5:443;;若写成 HTTP,proxy_ssl_*指令将被忽略
应对前端 WAF/CDN 覆盖 Host 头的情况
若 WSS 请求先经过 Cloudflare、阿里云 WAF 或 API 网关,它们常重写 Host 头为源站名(如 origin.example.com),导致 $host 失真、SNI 错配:
FastAPI + Flask 混合部署最佳实践,解决路由定义、API 代理等常见问题,适用于同时运行 FastAPI API 与 Flask 前端的场景。
- 在 CDN/WAF 控制台开启“透传原始 Host”功能,通常会自动带上
X-Forwarded-Host头 - Nginx 中改用
proxy_ssl_name $http_x_forwarded_host; - 同时补上
proxy_set_header X-Forwarded-Host $host;,便于后端审计或路由
upstream 场景下必须用字符串字面量
当使用 upstream 定义多个 WSS 后端节点时,SSL 握手阶段无法解析变量,$host 会失效或报错:
- 定义 upstream 时仅写 IP 或内部域名:
upstream wss_cluster { server wss-01.internal:443; } - 在
location中显式指定 SNI 字符串:proxy_ssl_name "app.example.com";(带双引号,大小写敏感) - 该值必须与 WSS 服务端证书的 SAN(Subject Alternative Name)完全一致,不能是泛域名通配符本身(如填
myapp.prod.example.com,而非*.prod.example.com)
验证 SNI 是否真正发出
配置无误 ≠ 实际生效。需确认 TLS 握手时 Client Hello 中的 SNI 字段确实是目标域名:
- 在 WSS 后端服务器(如另一台 Nginx 或自建网关)开启 debug 日志:
error_log /var/log/nginx/error.log debug;,搜索日志中client sent server name行 - 用 OpenSSL 手动测试:
openssl s_client -connect wss-backend-ip:443 -servername app.example.com 2>/dev/null | openssl x509 -noout -text | grep -A1 "Subject Alternative Name",确认返回证书包含该域名 - 抓包验证:在 Nginx 机器执行
tcpdump -i any 'port 443 and host wss-backend-ip' -w wss-sni.pcap,Wireshark 中检查 Client Hello 的server_name扩展字段










