群晖相关

dsh 公网访问时“工作区加载不出来”排查实录:都是 WebSocket 惹的祸

2026-08-15 16 次阅读 1875 字 · 5 分钟

dsh 公网访问时"工作区加载不出来"排查实录:都是 WebSocket 惹的祸



> 前置阅读:群晖 Docker 部署 DeepSeek Harness(dsh)完整教程


现象



在群晖 Docker 里部署了 DeepSeek Harness(dsh),局域网访问一切正常,但一旦从公网域名(比如 https://你的域名.com,经 NPS 隧道 + 群晖 DSM 反向代理)访问时,出现:

  • 页面能打开,登录也正常;

  • 但侧边栏工作区列表一直是空的,点"选择工作区"目录也列不出来;

  • 浏览器控制台报 transport failure for /api/...: HTTP 403 或 WebSocket 连接失败。


  • 排查结论(先给答案)



    这类问题通常不是 dsh 本身坏了,而是三层各自的安全/转发机制在拦,按顺序排查:

  • 浏览器安全上下文 → crypto.randomUUID is not a function

  • dsh 的 /api 信任栅栏 → HTTP 403

  • 反向代理不转发 WebSocket → HTTP 426 Upgrade Required(本篇重点)




  • 第一层:crypto.randomUUID 报错



    现象:局域网 IP 用明文 HTTP 访问时,点"选择工作区"报:

    crypto.randomUUID is not a function
    


    原因:这个浏览器 API 只在安全上下文(HTTPS 或 localhost)里存在,明文 HTTP + 局域网 IP 不满足。

    解决:

  • 用 HTTPS 访问;或

  • 在反代层用 sub_filter 往页面里注入一个 UUID v4 补丁(dsh 场景下可用),普通 HTTP 也能用。




  • 第二层:/api 信任栅栏 403



    现象:换了个新域名访问后,工作区/目录接口全部 HTTP 403,但页面能开。

    原因:dsh 出于防 DNS rebinding / 跨站请求的安全考虑,/api 只接受它认可的 Host(回环地址、部署 IP、或显式登记的 --trusted-host)。新域名不在名单里就被拒。

    解决:启动 dsh 时把域名加进白名单:

    dsh web --host 127.0.0.1 --port 3080 \
      --trusted-host 你的域名.com \
      --trusted-host 你的公网IP:3080
    


    注意:--trusted-host 只接受 host 或 host:port 的规范写法,端口写法必须与浏览器访问的一致。



    第三层:反向代理不转发 WebSocket(本篇重点)



    现象特征



    页面 200、普通接口 200,但浏览器里 dsh 的实时数据(工作区/会话列表)永远加载不出来。用命令行模拟 WebSocket 升级一眼就能定位:

    curl -i \
      -H "Connection: Upgrade" \
      -H "Upgrade: websocket" \
      -H "Sec-WebSocket-Version: 13" \
      -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
      https://你的域名.com/api/events.mux
    


    如果返回:

    HTTP/1.1 426 Upgrade Required
    Server: nginx
    


    就是反向代理把 WebSocket 升级请求拦了。

    为什么会这样



    dsh 的 Web UI 靠两条 WebSocket 通道(/api/events.mux、/api/events.host)实时推送工作区/会话数据。普通 HTTP(S) 反向代理如果不带以下转发头,浏览器发起的 Upgrade: websocket 请求就不会被代理给后端:

    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    


    特别是群晖 DSM 自带的反向代理(控制面板 → 登录门户 → 反向代理)默认不带这两行,这是它的已知短板。

    解决办法



    #

    方案 A:给反向代理加 WebSocket 转发头(DSM 实测有效)



    SSH 登录群晖,编辑 DSM 的 nginx 配置(改前先备份):

    sudo cp /usr/local/etc/nginx/sites-enabled/server.ReverseProxy.conf \
            /usr/local/etc/nginx/sites-enabled/server.ReverseProxy.conf.bak
    


    在对应域名的 location / { } 块里加上:

    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 3600s;
    proxy_send_timeout 3600s;
    


    然后测试并重载:

    sudo nginx -t
    sudo nginx -s reload
    


    改完刷新页面,工作区列表就出来了。

    > ⚠️ 注意:DSM 界面里如果再修改反向代理设置,它会重新生成配置、覆盖手工修改,需要重新加这两行。

    #

    方案 B:换一个支持 WebSocket 的入口



  • Cloudflare Tunnel(推荐):域名走 Cloudflare,原生支持 WebSocket 和 HTTPS 证书,换后端也不影响;

  • 或者自己跑一个 nginx 容器做 TLS + 反代,配置里带上上面那几行。




架构备注:NPS 隧道是透明的



排查过程中容易误判"NPS 是不是不支持 WebSocket"。NPS 的 TCP 隧道是纯透明转发,WebSocket 可以原样穿过(用 NPS 直连隧道入口实测升级是 101 Switching Protocols)。

所以:只要 426 出现在"带域名反代"的路径上,问题一定在反代层,不在 NPS。



快速自检清单



现象原因处理
crypto.randomUUID is not a function非安全上下文HTTPS 访问,或注入补丁
/api 全部 403域名不在信任白名单加 --trusted-host
WebSocket 426反代不带转发头加 Upgrade/Connection 两行
一切正常但列表空工作区还没创建会话先"选择工作区"选目录开个会话




小结



dsh 这类"页面能开、数据不来"的问题,九成是反向代理把 WebSocket 拦了。用一条 curl 模拟升级请求就能快速区分是 403(信任栅栏)还是 426(反代不支持 WS),对症下药,十分钟收工。

评论(0)

8 + 3 =
← 返回首页