wss服务必须使用wss://协议,服务端需显式监听tls端口、配置含"ws"的nextprotos、加载完整证书链并确保私钥权限合规,缺一不可。

WSS服务必须用 wss://,不是加个TLS配置就能自动升级
很多人误以为只要给HTTP服务器配了TLS证书,WebSocket连接就自动变安全——其实完全不是。WSS(WebSocket Secure)本质是运行在TLS之上的WebSocket协议,它要求客户端显式使用 wss:// 协议发起连接,服务端也必须监听在TLS端口(如443或8443),且路由逻辑要能处理升级请求(Upgrade: websocket)。单纯在HTTP服务上套TLS,不处理WebSocket握手,客户端连 wss:// 会直接返回400或500。
http.Server 的 TLSConfig 必须显式传入,不能依赖 http.ListenAndServeTLS
Go的 http.ListenAndServeTLS 内部会新建一个 http.Server 并设置默认 TLSConfig,但这个默认配置无法覆盖WebSocket场景所需的细节,比如ALPN协商、SNI路由、或自定义证书验证逻辑。真实WSS服务应手动构造 http.Server 实例,并把定制的 tls.Config 赋给其 TLSConfig 字段:
srv := &http.Server{
Addr: ":8443",
TLSConfig: &tls.Config{
Certificates: []tls.Certificate{cert},
MinVersion: tls.VersionTLS12,
// 必须启用ALPN,否则wss握手失败
NextProtos: []string{"h2", "http/1.1", "ws"},
},
}
srv.ListenAndServeTLS("server.crt", "server.key")
-
NextProtos中必须包含"ws"(不是"wss"),这是RFC 7639规定的ALPN标识符,用于TLS握手阶段协商上层协议 - 若后端需支持HTTP/2和WebSocket共存,
"h2"和"http/1.1"也要保留 - 漏掉
NextProtos或写错值,浏览器发起wss://连接时会卡在TLS握手,报错类似net::ERR_SSL_PROTOCOL_ERROR
证书加载失败的三个高频原因
tls.LoadX509KeyPair 报错不是“文件不存在”就完事了,常见真问题藏在细节里:
- 私钥文件被密码保护:Go不支持解密加密的PEM私钥,必须用
openssl rsa -in server.key.enc -out server.key去密 - 证书链不完整:若用Let’s Encrypt等中间CA签发,
server.crt必须拼接完整链(域名证书 + 中间证书),否则某些客户端(尤其是iOS Safari)会拒绝信任 - 权限问题:Linux下若用非root用户运行,确保Go进程对
server.crt和server.key有读权限,且私钥文件权限不能大于0600(否则LoadX509KeyPair直接拒绝加载)
开发调试时别信 InsecureSkipVerify: true
本地跑WSS常遇到证书域名不匹配(比如用 localhost 证书但访问 127.0.0.1),有人直接在 tls.Config 里设 InsecureSkipVerify: true ——这会让整个TLS通道失去意义,且现代浏览器(Chrome 119+、Firefox 120+)已强制禁用该选项,即使服务端开了,客户端也会直接断连。正确做法是:
- 生成证书时用通配符或多个SAN(Subject Alternative Name),例如
openssl req -subj "/CN=localhost" -addext "subjectAltName = DNS:localhost,IP:127.0.0.1" - 或改用
mkcert工具本地签发受系统信任的证书,避免手动绕过验证
WSS的安全性不在“能不能连上”,而在“连上的那一刻是否真正加密且可验证”。证书路径、ALPN、私钥格式、域名匹配——四个点里任一出错,都可能让看似工作的连接实际走明文。











