同一个 Web UI,有人打不开首页,有人首页正常却调用 API 失败。它们可能发生在两层不同的检查上:浏览器有没有有效认证,以及这次 API 请求来自受信任的访问地址。
本文以 0.2.0-rc.2 的 Web 启动参数、浏览器认证 和 API 信任判断为依据。验证环境为 Node v24.19.0、独立 DSH_HOME;分别启动带与不带额外信任的 Web 实例,完成 14 项真实 HTTP 检查,未调用模型。
第一层:启动 token 换浏览器 cookie
按照安装篇运行:
npx --no-install dsh web --no-open --port 3099
首次访问使用终端打印的完整 URL。token 的用途是引导浏览器完成认证:服务端设置签名 cookie,再重定向到不含 token 的地址。浏览器随后通过 cookie 访问。
本版本的 cookie 默认有效期为 30 天,带有签名并绑定访问 authority,即 host 与端口组合。127.0.0.1:3099、localhost:3099 和 127.0.0.1:3100 不应被当成同一个认证入口。
因此,“换了端口后重新认证”并不是旧 cookie 被全局销毁,而是旧 cookie 不适用于新的 authority。重启后能否继续使用 cookie,还依赖原来的凭据存储和认证签名密钥是否保留;清空或更换 DSH_HOME 可能改变这个条件,不能只看 30 天的时间窗口。
第二层:有效 cookie 也不能绕过 API 信任检查
API 层还要核对 Host,以及请求携带的 Origin、Fetch Metadata 等信息。它在处理 DNS rebinding 和跨站请求风险,与身份认证职责不同。
下面是两次真实启动中的部分对照结果。API 请求均使用 POST /api/settings/describe;自定义 Host 的请求先换取属于该 authority 的有效 cookie,避免把认证失败混进信任检查:
| 请求条件 | 是否添加自定义 Host 信任 | 实际 HTTP 状态 |
|---|---|---|
| 裸首页,无 cookie | 否 | 401 |
| 启动 token 交换 cookie | 否 | 303 |
| loopback API,有有效 cookie | 否 | 200 |
| 自定义 Host 交换 cookie | 否 | 303 |
| 自定义 Host API,有对应 cookie | 否 | 403 |
| 同一自定义 Host API,有对应 cookie | 是 | 200 |
| 已信任 Host API,带跨站标记 | 是 | 403 |
用 localhost 对比有没有 --trusted-host,两边都成功,是因为默认信任范围已经包含 localhost。要测该参数,应选择一个原本不受信任的 authority,再单独检查登录和 API 请求。Origin 不匹配也会被信任函数拒绝,这一条件已用旧版本函数直接检查,本表没有把它混作新版 HTTP 实测结果。
同时,拿到 token 后能交换 cookie,并不自动证明该 Host 的 API 请求已经被允许。首页、认证交换和 API 不是同一个验证点。
先按状态定位,再看具体请求
| 现象 | 优先检查 |
|---|---|
| 首页或 API 返回 401 | token、cookie、访问 authority 与认证存储 |
| 页面能显示,但 API 返回 403 | Host 白名单、Origin 和跨站标记 |
请求 /api 返回 404 |
它可能只是路由前缀,不能据此断言认证规则 |
| 代理后某些功能失败 | 同时检查认证、信任、路由转发和 WebSocket |
状态码是排查线索,最终仍应读取实际响应与对应版本实现。要验证 API,使用该版本真实存在的端点;不要一直请求 /api 根路径。
一个可核对的只读端点示例来自上游认证测试:POST /api/settings/describe。它使用结构化的 client-request 信封,不是向任意路径发送一个空 GET。涉及 HTTP 的测试要分别记录响应状态、认证是否成立和信任条件,避免把某一次拒绝全部归因于 Host。
远程使用,先考虑 SSH 转发
单人远程开发时,可以让服务继续绑定远端 loopback,再通过 SSH 将端口带到本机。例如在本机运行,主机名替换成自己的远程机器:
ssh -N -L 3099:127.0.0.1:3099 user@your-remote-host
远端先使用相同的端口启动服务,本机再访问转发后的地址。浏览器侧使用哪个 host 和端口,就按那个 authority 完成认证;如果转发时改了端口,也要检查浏览器请求实际携带的 Host。
SSH 转发保护的是到远端监听口的连接路径。它不自动提供多用户工作区隔离,也不意味着可以把带 token 的地址随意共享。这个进程能够操作的文件和工具权限,仍属于运行它的环境。
反向代理需要按完整链路验收
对自定义域名,--trusted-host 声明 API 允许的 authority,但它不是登录凭据,也不会自动设置 TLS、改写 cookie、转发 WebSocket 或解决路径前缀。
0.2.0-rc.2 的 Web 帮助没有主干后来增加的某些部署参数,且仍明确拒绝 --host 0.0.0.0。因此,本篇不把最新主干的部署命令直接放进这个旧发布版本的教程。需要反向代理时,先对齐运行版本与文档,再逐项测试完整认证、API、WebSocket 和权限边界。
排错时最有用的记录是:CLI 版本、DSH_HOME 是否变化、浏览器可见的 authority、后端收到的 Host/Origin、失败端点和状态码。记录这些即可,不需要公开 token、cookie 或凭据文件。
回到首次运行检查基本路径;需要无人值守的调用时,优先比较headless与SDK,不必为了脚本调用先给 Web UI 配一层公网代理。