You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 19:02:51