Nginx 代理 WebSocket 失败排查全过程:从 426 到连接成功的完整复盘

  • 🧩 问题现象
  • 🔍 初步怀疑:Nginx 配置错误?
  • 🕵️‍♂️ 关键转折:前端页面来源被忽略
    • ❌ 问题本质
  • ✅ 解决方案
    • 方法 1:显式指定虚拟机 IP(临时可用)
    • 方法 2:使用相对路径(推荐 ✅)
  • 🛠 附:正确的 Nginx 配置模板
  • 💡 经验总结
  • 📌 最后提醒
  • ✅ 结语


关键词:Nginx、WebSocket、Docker、426 Upgrade Required、前端连接失败、代理配置

在开发基于 WebSocket 的实时应用时,使用 Nginx 作为反向代理是常见做法。可是我遇见了后端直连 WebSocket 成功,通过 Nginx 代理却失败。本文将完整复盘一次真实排查过程,带你避开所有“坑”。


🧩 问题现象

  • 后端服务运行在 192.168.1.10:8080,提供 WebSocket 接口 /ws/xxx
  • 使用 wscat 或浏览器直连 ws://192.168.1.10:8080/ws/xxx ✅ 成功
  • 配置 Nginx 反向代理后,前端连接 ws://localhost/ws/xxx ❌ 失败
  • 浏览器 Network 面板显示 WebSocket 请求失败(无 101 响应)
  • 关键线索:Nginx 的 access_log没有任何 WebSocket 请求日志

🔍 初步怀疑:Nginx 配置错误?

首先检查 Nginx 配置:

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

upstream webservers {
    server 192.168.1.10:8080;
}

server {
    listen 80;
    server_name localhost;

    location /ws/ {
        proxy_pass http://webservers/ws/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "$connection_upgrade";  # ⚠️ 这里有坑!
        proxy_read_timeout 3600s;
    }
}

常见错误包括:

  • Connection 头加了双引号 → 变成字面量
  • map 没放在 http 块顶层
  • 缺少 proxy_buffering off

但即使修正这些,日志依然为空,说明:请求根本没到 Nginx!


🕵️‍♂️ 关键转折:前端页面来源被忽略

通过 docker ps 确认端口映射正常:

nginx     0.0.0.0:80->80/tcp

但发现一个致命细节:

前端页面是通过虚拟机 IP 打开的:http://192.168.228.128/
而 WebSocket 地址写的是:ws://localhost/ws/xxx

❌ 问题本质

  • 浏览器中的 localhost 永远指向客户端本机(Windows 电脑)
  • 而我的 Nginx 运行在 虚拟机中
  • 所以 ws://localhost/... 实际请求的是 我 Windows 的 80 端口,而非虚拟机!

→ 请求压根没发给 Nginx,自然没有日志!


✅ 解决方案

方法 1:显式指定虚拟机 IP(临时可用)

// 前端 JS
const ws = new WebSocket('ws://192.168.228.128/ws/xxx');

✅ 立即生效!

方法 2:使用相对路径(推荐 ✅)

// 自动继承当前页面的 host
const ws = new WebSocket('/ws/xxx');

优势:

  • 页面在 http://localhost → 连 ws://localhost
  • 页面在 http://192.168.228.128 → 连 ws://192.168.228.128
  • 部署到域名也无需修改代码

🛠 附:正确的 Nginx 配置模板

http {
    # 必须在 http 块顶层!
    map $http_upgrade $connection_upgrade {
        default upgrade;
        '' close;
    }

    upstream backend {
        server host.docker.internal:8080;  # Docker Desktop 推荐
        # 或 server 192.168.1.10:8080;
    }

    server {
        listen 80;
        server_name _;  # 匹配任意 Host

        location / {
            root /usr/share/nginx/html;
            index index.html;
        }

        location /ws/ {
            access_log /var/log/nginx/ws_access.log;
            proxy_pass http://backend/ws/;
            proxy_http_version 1.1;
            proxy_buffering off;          # 必须关闭
            proxy_cache off;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;  # 无引号!
            proxy_read_timeout 3600s;
        }
    }
}

💡 经验总结

问题类型 排查要点
Nginx 无日志 先确认请求是否真的发到了 Nginx(检查前端地址、端口映射)
WebSocket 426 检查 UpgradeConnection 头是否正确传递
前端连接失败 确保 WebSocket 地址与页面同源(避免硬编码 localhost
Docker 环境 host.docker.internal 代替 IP 更可靠

📌 最后提醒

localhost 在前端代码中 ≠ 你的服务器!
它永远指向用户浏览器所在的机器

开发时务必注意页面来源与 WebSocket 目标的匹配关系,这是 WebSocket 代理失败的最高频原因


✅ 结语

从“426 Upgrade Required”到“连接成功”,看似是配置问题,实则是网络拓扑理解偏差。希望本文能帮你少走弯路!

欢迎点赞、收藏、评论交流!
如果你也在 Docker + Nginx + WebSocket 中踩过坑,欢迎留言分享你的故事!

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐