最近在给客户部署实时数据大屏时,遇到了一个典型问题:前端通过WebSocket请求数据时,浏览器总是弹出安全警告。这是因为我们直接使用了WS协议(ws://),这种明文传输方式在现代浏览器中会被视为不安全连接。更麻烦的是,当用户量突然增加时,单台WebSocket服务器很快就扛不住了。
这时候Nginx的WSS反向代理就成了救命稻草。WSS(WebSocket Secure)本质就是WebSocket over TLS,就像HTTPS是HTTP的安全版一样。实测下来,这种方案能同时解决三个痛点:
去年我们有个在线教育项目,就因为没做WSS导致上课时的画笔轨迹数据被中间人截获。后来用Nginx改造后,不仅安全性提升了,还意外解决了iOS设备上的兼容性问题——苹果设备对非安全WebSocket的限制特别严格。
先确认你的机器上已经装好了Nginx,我习惯用这个命令检查版本:
bash复制nginx -v
建议使用1.14+版本,老版本对WebSocket的支持可能有坑。遇到过最奇葩的情况是CentOS默认源里的1.12版本会导致WebSocket连接随机断开,升级后立即解决。
SSL证书是WSS的核心,有三种获取方式:
用OpenSSL生成自签名证书的命令:
bash复制openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
这个命令会生成两个文件:
建议把证书文件放在/etc/nginx/ssl目录下,记得设置权限:
bash复制chmod 400 /etc/nginx/ssl/*
有次我们的证书被意外覆盖导致全线服务中断,后来我养成了备份证书的习惯。可以用这个命令验证证书有效性:
bash复制openssl x509 -in cert.pem -text -noout
不要在nginx.conf里直接修改,推荐在conf.d目录新建websocket.conf。这是我的标准模板:
nginx复制map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 443 ssl;
server_name yourdomain.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
location /ws {
proxy_pass http://backend_ws;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
}
}
几个关键点解释:
map指令处理连接升级逻辑proxy_http_version 1.1:必须声明HTTP 1.1Upgrade和Connection头是WebSocket握手的关键生产环境还需要这些配置:
nginx复制proxy_read_timeout 86400s; # 长连接超时时间
proxy_send_timeout 86400s;
proxy_buffer_size 16k;
proxy_buffers 4 64k;
proxy_busy_buffers_size 128k;
曾经有个物联网项目因为没设proxy_read_timeout,导致设备每隔5分钟就重连。这些值要根据业务特点调整:
先测试基础HTTPS:
bash复制curl -v https://yourdomain.com
再测试WebSocket升级协议:
bash复制curl --include \
--no-buffer \
--header "Connection: Upgrade" \
--header "Upgrade: websocket" \
--header "Host: yourdomain.com" \
--header "Sec-WebSocket-Version: 13" \
https://yourdomain.com/ws
连接立即断开:
检查Nginx错误日志:
bash复制tail -f /var/log/nginx/error.log
常见错误:
跨域问题:
在location块添加:
nginx复制add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
对于高并发场景:
nginx复制events {
worker_connections 1024;
use epoll;
}
http {
proxy_temp_path /dev/shm/nginx_temp;
client_body_buffer_size 10K;
client_header_buffer_size 1k;
}
把临时目录挂载到内存(/dev/shm)能显著提升性能,实测QPS从3000提升到8500+。
建议配置Prometheus监控:
nginx复制location /metrics {
stub_status on;
access_log off;
}
关键指标:
去年帮一个跨境电商升级客服系统时,原有架构是这样的:
code复制客户端 → WS → 单台Node.js服务器
问题频发:
改造后的架构:
code复制客户端 → WSS → Nginx → WS → 多台Node.js
具体配置:
nginx复制upstream chat_nodes {
zone backend 64k;
server 10.0.1.101:3000 weight=5;
server 10.0.1.102:3000 weight=5;
server 10.0.1.103:3000 weight=1;
keepalive 32;
}
效果提升:
WebSocket服务的更新比较麻烦,我常用的灰度方案:
nginx复制split_clients "${remote_addr}${http_user_agent}" $variant {
10% "v2";
90% "v1";
}
upstream v1 { server 10.0.0.1:3000; }
upstream v2 { server 10.0.0.2:3000; }
location /ws {
proxy_pass http://$variant;
}
这个配置能实现:
除了基础SSL,还需要:
nginx复制# 禁用不安全的协议
ssl_protocols TLSv1.2 TLSv1.3;
# 启用HSTS
add_header Strict-Transport-Security "max-age=63072000" always;
# 防止点击劫持
add_header X-Frame-Options DENY;
特别提醒:WebSocket容易成为DDoS目标,建议配合rate limiting:
nginx复制limit_req_zone $binary_remote_addr zone=wslimit:10m rate=30r/s;
location /ws {
limit_req zone=wslimit burst=50;
}
前端连接示例:
javascript复制const socket = new WebSocket('wss://yourdomain.com/ws');
socket.onerror = (error) => {
console.error('连接错误:', error);
// 实现自动重连逻辑
setTimeout(() => connect(), 5000);
};
iOS的坑点:App Transport Security要求:
xml复制<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoadsInWebContent</key>
<true/>
</dict>
这套配置稍作修改就能用于:
最近帮一个智慧农场项目做的优化:
nginx复制proxy_read_timeout 1800s;
proxy_send_timeout 1800s;
proxy_set_header X-Device-ID $http_x_device_id;
设备离线后能在30分钟内恢复连接,同时通过设备ID做会话保持。