一篇读懂 WebSocket 与 WSS:从原理到实战避坑指南
别再让你的 WebSocket 裸奔了,是时候用 WSS 武装起来。
前言:一个常见的翻车现场
小明是个前端新手,高高兴兴开发了一个实时聊天室。本地 localhost 跑得飞起,部署到云服务器后,控制台却弹出一个刺眼的红色报错:
Mixed Content: The page at ‘https://example.com’ was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint ‘ws://example.com/ws’. This request has been blocked.
明明本地好好的,怎么一上线就崩了?
其实,这就是 WS 和 WSS 没搞明白的经典翻车案例。今天这篇文章,就带你彻底搞懂 WebSocket 和 WSS 之间的关系、区别,以及如何优雅地避开所有坑。
一、WebSocket 是什么?它为什么重要?
在 WebSocket 诞生之前,HTTP 协议有一个“傲娇”的毛病:只能由客户端发起请求,服务器不能主动找客户端聊天。
为了实现“服务器推送”(比如股票行情、弹幕、游戏同步),开发者们曾用 轮询(Polling) 和 长轮询(Long-Polling) 来曲线救国。但这些方案本质上还是客户端不停发请求,造成了巨大的网络开销。
WebSocket 的出现就是为了打破这种僵局。 它通过一次 HTTP 握手,将连接升级为全双工通信。之后,客户端和服务器就可以随时互发消息,就像打电话一样自然。
二、WS 与 WSS:就像 HTTP 与 HTTPS 一样
从本质上看,WS(WebSocket)和 WSS(WebSocket Secure)的关系,完完全全等同于 HTTP 和 HTTPS 的关系。
- WS 是明文传输。
- WSS 是加密传输(基于 TLS/SSL)。
我们可以通过一张表格快速了解它们的核心差异:
| 对比维度 | WS (ws://) | WSS (wss://) |
|---|---|---|
| 安全性 | 明文传输,数据裸奔,可被中间人窃听或篡改 | 加密传输,具备机密性、完整性校验和身份验证 |
| 默认端口 | 80(与 HTTP 一致) | 443(与 HTTPS 一致) |
| URL 示例 | ws://example.com/socket |
wss://example.com/socket |
| 性能开销 | 低(无加解密计算,速度最快) | 略高(需要 TLS 握手加解密,CPU 负载增加) |
| 适用场景 | 本地开发、内网环境、IoT 低功耗设备 | 所有公网/生产环境、涉及敏感数据的场景 |
三、深入原理:WSS 是如何保护数据的?
当你使用 WSS 时,数据包在发送前会经历以下过程:
- 握手升级:客户端发起 HTTPS 请求,携带
Upgrade: websocket头部。 - TLS 加密层:在 TCP 和 WebSocket 协议之间,插入一层 TLS/SSL 加密协议。
- 加密传输:所有数据帧都会被加密成乱码。即便黑客截获了数据包,看到的也只是一堆无法识别的二进制密文。
- 身份验证:WSS 依赖数字证书(CA 证书),客户端可以确认自己连接的确实是合法的服务器,从而避免被“钓鱼”或劫持。
反观 WS:它只发送裸数据。如果你在公共 WiFi 下使用 WS,同一网络下的任何人用 Wireshark 都能直接看到你的聊天内容和传输的 Token。
四、浏览器环境下的“铁律”(必看!)
这是所有 Web 开发者必须死记硬背的规则。浏览器有一个严格的混合内容策略(Mixed Content Policy):
- 如果当前页面地址是
https://,那么绝对不允许使用ws://发起连接。 - 浏览器会直接拦截请求并报错,不会给你任何商量的余地。
变通方案:
| 页面协议 | 是否允许 ws:// |
是否允许 wss:// |
结论 |
|---|---|---|---|
| HTTPS | ❌ 禁止(被浏览器拦截) | ✅ 允许 | 生产环境必须用 WSS |
| HTTP | ✅ 允许 | ✅ 允许 | 但不推荐,建议全站 HTTPS |
| Localhost | ✅ 允许 | ✅ 允许 | 本地调试可用 WS 省资源 |
五、后端如何适配 WSS?(代码实战)
后端要支持 WSS,通常只需要做两件事:配置证书 和 修改监听端口。
情况一:Node.js + WebSocket 库 (ws)
const fs = require('fs');
const https = require('https');
const WebSocket = require('ws');
// 1. 读取证书文件(从阿里云/腾讯云/Let's Encrypt 获取)
const server = https.createServer({
cert: fs.readFileSync('/path/to/cert.pem'),
key: fs.readFileSync('/path/to/key.pem')
});
// 2. 将 WebSocket 附着在 HTTPS 服务器上
const wss = new WebSocket.Server({ server });
wss.on('connection', (ws) => {
console.log('WSS 连接已建立');
ws.send('Hello, 加密世界!');
});
// 3. 监听 443 端口(或其它)
server.listen(443);
情况二:Nginx 反向代理(推荐方案)
如果你的业务后端本身是 HTTP(即 ws://),可以用 Nginx 做“SSL 终结”:
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location /websocket/ {
proxy_pass http://backend_server:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
# WSS 的关键:告诉后端原来是 WSS 过来的
proxy_set_header X-Forwarded-Proto $scheme;
}
}
这样一来,后端无需修改任何业务代码(依然是 WS),但对外提供的就是 WSS 加密连接。
六、关于端口 443 的误区
很多人以为 WSS 必须跑在 443 端口。其实不是的。
WSS 协议本身允许跑在任何端口(比如 wss://example.com:8443)。但为什么大家都用 443?
- 防火墙穿透性:443 是 HTTPS 的默认端口,防火墙通常不会拦截 443 出站流量。如果你选了个冷门端口(如 8443),在公司的严格网络环境下,连接可能被阻断。
- URL 简洁:不用在 URL 里额外写端口号。
注意:如果 WSS 跑在非 443 端口,浏览器依然要求必须配好证书,否则会提示不安全。
七、常见报错与排查指南
| 报错信息 | 可能原因 | 解决方案 |
|---|---|---|
Mixed Content |
HTTPS 页面请求了 WS | 将前端代码改为 wss:// |
WebSocket connection to 'wss://...' failed |
证书无效或证书与域名不匹配 | 检查证书是否过期,域名是否包含在证书 SAN 中 |
SSL handshake error |
后端只开了 HTTP,但客户端请求 WSS | 后端需配置证书开启 HTTPS,或使用 Nginx 代理 |
Connection refused |
端口未开放 / 防火墙拦截 | 检查云服务商安全组,放行对应端口 |
八、总结与建议
- 生产环境无脑选 WSS。这是现代 Web 开发的绝对标准,不仅是安全需求,也是浏览器兼容性的硬性要求。
- 不要裸奔。如果你的聊天室、游戏或 IoT 设备还在用 WS 传输 Token 或用户隐私,那无异于在广场上喊密码。
- 利用 Nginx 降本增效。如果后端改造麻烦,用 Nginx 做 TLS 终结是最快、最优雅的 WSS 部署方案。
- 本地开发可以用 WS,但建议开发时就习惯配置
wss://localhost,可以避免上线时遗漏修改。
下次上线前,记得检查一下你的 WebSocket 地址:如果开头不是 wss://,那可就要小心“裸奔”了! 希望这篇文章能帮你避开小明踩过的坑,如果有任何问题,欢迎在评论区交流讨论。

浙公网安备 33010602011771号