wss服务端不能用http.listenandservetls,因其仅处理http/https流,遇upgrade请求返回400/426而不进handler;必须用tls.listen创建listener、配tls.config的http.server、再显式server.serve(),且证书须用tls.loadx509keypair加载完整链并确保私钥权限≤0600。
wss 服务端不能用 http.listenandservetls,必须手动构造 http.server + tls.config 并用 tls.listen 启动,否则浏览器 websocket 握手直接报 net::err_ssl_protocol_error。
为什么 ListenAndServeTLS 不适用于 WSS
标准 http.ListenAndServeTLS 内部只处理 HTTP/HTTPS 请求流,遇到 Upgrade: websocket 请求时会返回 400 或 426,根本不会走到你的 handler。WSS 要求 TLS 连接建立后,在同一连接上完成协议切换——这需要你显式控制 TLS listener 生命周期。
-
ListenAndServeTLS是封装好的“开箱即用”逻辑,不暴露底层net.Listener,无法介入 Upgrade 流程 - gorilla/websocket 等库的
Upgrader.Upgrade()必须在已加密的net.Conn上调用,而ListenAndServeTLS不提供该 Conn 的访问入口 - 即便路由注册了
http.HandleFunc("/ws", ...),请求也卡在 HTTP server 的预检阶段
正确启动 WSS 服务的三步操作
核心是:用 tls.Listen 创建 listener → 构造带 TLSConfig 的 http.Server → 显式调用 server.Serve()。
- 证书加载必须用
tls.LoadX509KeyPair(certFile, keyFile),不能传单文件;certFile要含完整链(服务器证书 + 中间 CA),顺序不能颠倒 -
keyFile必须是未加密私钥(无DEK-Info头),Linux/macOS 下权限必须 ≤0600,否则tls.Listen启动时 panic - Windows 路径写成
"C:/tls/cert.pem"或反引号原始字符串`C:\tls\cert.pem`,混用反斜杠会静默失败
客户端连 WSS 失败?先查 ServerName 和 SNI
Go 客户端(如 websocket.Dial)默认启用 SNI,并严格校验证书中的 DNSNames 或 IPAddresses 字段。访问 wss://localhost:8443 却报 x509: certificate is valid for example.com,说明证书没覆盖 localhost。
- 生成自签名证书时,
DNSNames至少包含"localhost";若需 IP 访问,还得加IPAddresses: []net.IP{net.ParseIP("127.0.0.1")} - 证书中
IsCA: true不是必须的(除非你用它签其他证书),但KeyUsageCertSign也不能乱设,否则校验失败 - 客户端用
websocket.DefaultDialer时,内部http.Client不读系统 CA,自签名证书必须通过GOCERTIFICATEPATH或显式RootCAs加载
最易被忽略的是证书链完整性与私钥权限——两者任一出错,服务可能启动成功但 WebSocket 握手静默失败,错误日志里甚至不出现明显线索。











