WSS连接失败但HTTPS能访问,主因是TLS终结位置错配、证书缺失SAN字段或WebSocket升级头未透传。需检查Go服务是否显式启用TLS、Nginx是否配置proxy_set_header Upgrade $http_upgrade等三行关键头,以及证书是否含subjectAltName且链完整。

Go 的 crypto/tls 本身不“开启 WSS”,WSS 是 WebSocket over TLS,本质就是复用 HTTPS 的 TLS 层——你配好 HTTPS,WSS 自然就通了;但证书配置稍有偏差,wss:// 就会直接被浏览器拒绝连接,且错误提示极不友好(比如只报“net::ERR_CONNECTION_CLOSED”)。
为什么 wss:// 连不上,但 https:// 能访问?
这是最常踩的坑:HTTPS 页面能打开,说明证书文件路径、格式、权限基本没问题;但 WSS 失败,往往卡在三个隐性环节:
-
http.Server启动时没显式启用 TLS,而是靠反向代理或 Nginx “偷偷”做了 TLS 终结——此时后端 Go 服务实际走的是明文ws://,浏览器收不到Upgrade: websocket+ TLS 握手响应,直接断连 - 证书中缺失
SAN(Subject Alternative Name),尤其是开发用localhost或内网域名时,Chrome / Edge 从 2021 年起强制要求证书必须含 SAN,否则拒绝建立 WSS 连接(哪怕 HTTPS 页面能渲染) - 客户端 JS 创建
WebSocket实例时用了ws://协议,而服务端监听的是 TLS 端口——协议不匹配,底层 TCP 连上也立刻被 reset
自签名证书生成必须带 SAN 和 IsCA:true
开发阶段用自签名证书测试 WSS,openssl 命令行容易漏掉关键参数。以下命令生成的证书才能被现代浏览器接受:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes -subj "/CN=localhost" -addext "subjectAltName = DNS:localhost,IP:127.0.0.1"
注意两点:
-
-addext "subjectAltName = ..."是 OpenBSD / macOS OpenSSL 1.1.1+ 才支持的写法;Linux 上若提示 unknown option,改用配置文件方式,否则浏览器会静默拒绝 WSS - Go 客户端代码若也要校验服务端证书(如代理场景),需手动把该
cert.pem加入tls.Config.RootCAs,且证书必须含IsCA: true字段,否则x509: certificate signed by unknown authority
ListenAndServeTLS 与自定义 tls.Config 的选择
简单服务直接用 http.ListenAndServeTLS 最快,但 WSS 场景下推荐显式构造 http.Server + tls.Config,原因很实际:
- 需要设置
GetCertificate支持 SNI(多域名共用一个 IP 时 WSS 必须) - 必须禁用弱密码套件:
MinVersion: tls.VersionTLS12,否则 IE11 / 旧 Android WebView 的 WSS 握手失败 - 若后端是反向代理(如转发 WSS 到另一个
wss://地址),需在http.Transport中配置TLSClientConfig,此时统一管理tls.Config更清晰
示例关键片段:
srv := &http.Server{
Addr: ":8443",
Handler: myMux,
TLSConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
CurvePreferences: []tls.CurveID{tls.CurveP256},
},
}
log.Fatal(srv.ListenAndServeTLS("cert.pem", "key.pem"))
WSS 升级请求被拦截的典型现象和检查点
浏览器控制台看不到明显报错,但 Network 面板里 WebSocket 请求状态始终是 (pending) 或直接 failed,常见于:
- Nginx / Caddy 做了 TLS 终结,但没透传
Upgrade和Connectionheader,导致 Go 服务收不到升级请求——必须加proxy_set_header Upgrade $http_upgrade;等三行配置 - Go 服务监听
:8443,但前端 JS 写的是wss://example.com/ws,而 DNS 解析或 Hosts 没指向该端口对应的服务 IP - 证书私钥文件被 chmod 600,但 Go 进程运行用户无读取权限(尤其容器内运行时 UID 不匹配)
真正难定位的是:证书链不完整。比如 cert.pem 只含站点证书,没拼入中间 CA 证书,某些系统(如 iOS Safari)WSS 会失败,而 Chrome 可能侥幸成功——务必用 openssl s_client -connect localhost:8443 -servername localhost 检查输出里的 Verify return code 是否为 0。


















