记录一个用 nginx 配置 websocket 和跨域的问题。

配置 websocket 反向代理

WebSocket 应用程序可以在客户端和服务端保持长连接,实现实时通信。并且 WebSocket 协议的握手与 HTTP 协议兼容,所以可以通过 HTTP 服务器代理 WebSocket 请求。

所以一开始的配置是直接基于 Nginx443端口,使用 proxy_pass 代理 WebSocket 请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server{

......

location /service {
proxy_pass http://localhost:9090;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;

# Required for web sockets to function
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

gzip off;
}
}

出于业务的需求,在原本的服务器块里加上一个新的 location 块,用于代理 WebSocket 请求。第一行的 location /service 是代理的路径,proxy_pass 后面的地址是 WebSocket 服务的地址。
然后设置 proxy_http_version1.1proxy_bufferingoffproxy_set_header Upgrade $http_upgradeproxy_set_header Connection "upgrade" 是为了让 WebSocket 协议生效。

解决跨域问题

在配置 WebSocket 代理的时候,发现 WebSocket 连接失败,控制台报错 WebSocket connection to 'ws://xxx' failed: Error during WebSocket handshake: Unexpected response code: 403。直接访问 WebSocket 服务的地址是可以正常连接的,走 443 端口的代理和走 80 端口的代理到 WebSocket 服务的地址都会出现这个问题。看了一下 Nginx 的日志,发现是被代理的服务端返回了 403 错误。对比了一下直接访问和代理访问的请求头,发现了一个问题,Origin 请求头和 Host 请求头不一样。判断大概率是后端服务做了跨域限制,所以需要在 Nginx 配置中加上跨域的配置。

location 块中加上 proxy_set_header Origin http://localhost:9090;,将 Origin 请求头设置为 http://localhost:9090,这样就可以解决问题了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
server{

......

location /service {
proxy_pass http://localhost:9090;
proxy_set_header Host localhost:9090;
proxy_set_header Origin http://localhost:9090;
proxy_set_header X-Forwarded-Proto $scheme;

# Required for web sockets to function
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

gzip off;
}
}