Docker化React Socket.io客户端经Nginx反向代理无法与Uvicorn ASGI服务器收发事件排查求助
问题排查与解决方案
从你的描述来看,核心问题出在Nginx反向代理对WebSocket/Socket.io请求的处理配置上——本地无Nginx时服务正常,说明业务代码本身没有问题,下面是具体的排查和修复步骤:
1. 修正Nginx的WebSocket转发配置
你的Nginx配置里有两个关键问题:
- 根路径
/里错误添加了WebSocket升级头,会干扰静态资源请求 /socket.io路径下的Connection头硬写为"upgrade",没有区分WebSocket和普通HTTP请求
修改你的default.conf为以下内容:
# Use site-specific access and error logs and don't log 2xx or 3xx status codes map $status $loggable { ~^[23] 0; default 1; } # 新增map:动态适配WebSocket与普通HTTP请求的Connection头 map $http_upgrade $connection_upgrade { default upgrade; '' close; } access_log /var/log/nginx/access.log combined buffer=512k flush=1m if=$loggable; error_log /var/log/nginx/error.log; upstream socket-server-upstream { server api:8080; keepalive 16; } server { listen 80; listen [::]:80; server_name example-site.com www.example-site.com; client_max_body_size 15M; location / { root /usr/share/nginx/html; index index.html index.htm; try_files $uri $uri/ /index.html; expires -1; # 移除此处WebSocket相关头,仅保留静态资源处理逻辑 } location /socket.io { proxy_pass http://socket-server-upstream/socket.io; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Host $host; # 新增:确保后端正确识别请求来源 proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; # 使用动态变量替代硬写值 proxy_redirect off; proxy_buffering off; proxy_http_version 1.1; # 强制使用HTTP/1.1,WebSocket依赖该版本协议 # 移除冗余CORS头:后端已设置cors_allowed_origins="*",重复设置可能引发冲突 } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } }
配置修改说明:
- 新增
map $http_upgrade $connection_upgrade:自动判断请求是否需要升级为WebSocket,非WebSocket请求会设置Connection: close,避免干扰正常HTTP请求 - 移除根路径下的WebSocket升级头:静态资源不需要这些头,反而可能导致请求异常
- 添加
proxy_http_version 1.1:WebSocket协议基于HTTP/1.1,必须明确指定才能正常转发 - 添加
proxy_set_header Host $host:确保Uvicorn能正确获取请求的主机名,避免Socket.io握手验证失败
2. 调整React客户端的Socket连接配置
你的客户端强制指定仅用websocket传输,且用ws://开头的URL,可能跳过Socket.io默认的HTTP握手阶段,导致事件无法传递。
修改withSocket.tsx的连接配置:
const socket: Socket<ServerToClientEvents, ClientToServerEvents> = io(process.env.REACT_APP_SOCKET_URL!, { transports: ["polling", "websocket"], // 恢复默认传输顺序:先HTTP握手再升级WebSocket path: "/socket.io", withCredentials: false, // 后端已允许所有CORS源,无需携带凭证 });
同时修改.env里的URL为HTTP协议:
REACT_APP_SOCKET_URL=http://example-site.com
调整说明:
- 恢复默认传输顺序:Socket.io需要先通过HTTP请求完成握手,再升级到WebSocket,强制只用websocket可能跳过握手步骤,导致事件无法传递
- 使用HTTP URL:Socket.io客户端会自动根据场景切换到ws/wss协议,比直接指定ws更可靠
3. 验证服务日志与连接状态
完成配置修改后,重新构建并启动服务:
docker compose down && docker compose up --build
然后检查以下日志:
- 查看Uvicorn服务日志,确认是否收到客户端连接和事件:
如果看到docker logs <api-container-name>connected <sid>说明连接成功;发送事件后无received data: xxx日志,说明Nginx未正确转发请求 - 查看Nginx错误日志,排查转发问题:
docker exec <client-container-name> cat /var/log/nginx/error.log
4. 检查DigitalOcean防火墙设置
确保你的Droplet防火墙允许80端口的TCP流量(WebSocket基于TCP协议,依赖80/443端口),避免流量被拦截。
总结
最核心的问题是Nginx的WebSocket转发配置不正确,导致Socket.io的事件请求无法正确传递到Uvicorn后端。按照上述步骤修正后,应该能解决事件发送/接收的问题。
内容的提问来源于stack exchange,提问作者MariO
相关产品推荐
相关产品推荐

