OpenClaw 升级到 2026.3.8 后 web_fetch 一直卡在“正在获取”的排查记录

问题现象

我把 OpenClaw 升级到 2026.3.8 之后,发现让它打开网页或查询网站时,经常会卡在下面这种状态,一直不返回:

1
2
正在获取...
web_fetch:27{"url":"https://docs.openclaw.ai/tools"}

我的部署方式是:

  • OpenClaw 本地运行

  • 前面用了 Caddy 做反向代理

  • 访问的是 OpenClaw 的 dashboard / Control UI

  • 目标网页例如:

    • https://docs.openclaw.ai/
    • https://docs.openclaw.ai/tools

先说结论

这类问题不太像是 2026.3.8 明确把 web_fetch 改坏了,更像是下面两类因素叠加:

  1. web_fetch 本身就存在“抓较大页面时可能无限挂起”的已知问题
  2. Caddy 反代场景下,Gateway 的 controlUi.allowedOriginstrustedProxies、转发头等配置不完整,导致前端和 Gateway 之间的 WebSocket / 工具结果流不稳定

换句话说:

  • 不是 release notes 里明说的 breaking change
  • 也不一定是 Caddy 单点配置错误
  • 更可能是 OpenClaw 的 web_fetch 已知问题 + 反代配置不完整 一起放大了症状。

2026.3.8 官方声明的改动

OpenClaw 2026.3.8 的 release notes 里,主要提到的是:

  • backup/create/verify
  • remote gateway token
  • talk silence timeout
  • TUI agent 推断

没有看到 web_fetch 的行为变更说明。

同时,官方文档仍然明确写着:

  • web_fetchHTTP fetch + readable extraction
  • 不会执行 JavaScript
  • 默认启用,除非显式禁用

这说明至少从文档层面看,2026.3.8 并没有把 web_fetch 下线或改成默认不可用。

真正可疑的点:web_fetch 已知会挂住

GitHub 上有一个已经打开的 issue,标题就很贴近这个现象:

web_fetch hangs indefinitely on large responses (~1MB)

里面描述得非常像遇到的问题:

  • web_fetch 对某些较大的页面响应会一直挂住
  • 即使设置了 maxChars
  • 日志里可能只看到 tool start
  • 没有 tool end

而 OpenClaw 文档站这种文档页面,本身就属于 HTML 内容比较大、提取流程比较复杂的页面类型。web_fetch 又只是做普通 HTTP GET 和正文提取,不是完整浏览器执行。

所以如果你看到的是:

  • 页面能打开 dashboard
  • 但一调用 web_fetch 就一直停在“正在获取…”
  • 没有明显报错

那就很像不是“功能没启用”,而是 工具调用已经发出,但工具结果没有正常结束返回

为什么 Caddy 也很值得怀疑

虽然 web_fetch 自身有已知问题,但这里前面还加了 Caddy 反代,这会让问题更难判断。

原因在于:dashboard 能打开,不代表 Control UI 和 Gateway 之间所有流式结果都一定正常。

OpenClaw 官方配置参考里明确写了:

  • controlUi.allowedOriginsGateway WebSocket connects 的显式浏览器 origin allowlist
  • 当浏览器客户端来自非 loopback origin 时,这是必需的

同时官方也明确写了:

  • trustedProxies 表示 终止 TLS 的反向代理 IP
  • 这里只应该写 你控制的反代 IP
  • allowRealIpFallback 默认是 false
  • 只有当 X-Forwarded-For 缺失时,才会在开启后接受 X-Real-IP

这意味着如果你把 OpenClaw 放在 Caddy 后面,至少要分清两件事:

  1. Gateway 监听在哪个地址上,供 Caddy 去访问
  2. Gateway 信任的是哪个代理来源 IP 段

这两者不是一回事。

关键点:customBindHosttrustedProxies 不是同一个 IP

这里一开始最容易混淆的是:

  • 172.17.0.1
  • 172.18.0.1

后来实际排查才发现:

  • host.docker.internal 在 Caddy 容器里解析到的是 172.17.0.1
  • 但 Caddy 所在 Docker 网络 caddy_network 的网关是 172.18.0.1
  • Caddy 容器自己的地址是 172.18.0.2

所以正确理解应该是:

  • customBindHost / Caddy 上游地址:看的是 Caddy 实际去连哪个宿主机地址
  • trustedProxies:看的是 Gateway 收到请求时,代理请求来自哪个 IP 段

于是最终更合理的组合反而是:

1
2
3
"bind": "custom",
"customBindHost": "172.17.0.1",
"trustedProxies": ["172.18.0.0/16"]

因为:

  • Caddy 实际反代到的是 172.17.0.1:18789
  • 但请求来源是 Caddy 容器所在网段 172.18.0.0/16

这也完全符合 OpenClaw 文档对 bindtrustedProxiesallowRealIpFallback 的定义。bind: "lan" 的含义是监听 0.0.0.0,而 bind: "custom" 才适合手动指定监听地址。

Caddy 这边最容易缺什么

一开始 Caddyfile 的配置大概是这样:

1
2
3
4
5
reverse_proxy https://host.docker.internal:18789 {
transport http {
tls_insecure_skip_verify
}
}

这能让 dashboard 打开,但不代表流式结果链路就一定完整。

在 OpenClaw 这种场景里,更建议显式写上:

1
2
3
4
5
6
7
reverse_proxy https://172.17.0.1:18789 {
transport http {
tls_insecure_skip_verify
}

header_up X-Forwarded-For {remote_host}
}

为什么最推荐的是这条?

因为 OpenClaw 网关在信任代理时,核心依赖的是 X-Forwarded-For;官方文档也强调了 trustedProxies 是给你信任的反代来源 IP 用的,而 allowRealIpFallback 默认关闭。换句话说,先把 X-Forwarded-For 这条链路理顺,优先级最高。

至于这些头:

1
2
3
header_up X-Real-IP {remote_host}
header_up X-Forwarded-Proto {scheme}
header_up Host {host}

不一定都必须手写。对 OpenClaw 来说,真正关键的是:

  • trustedProxies
  • controlUi.allowedOrigins
  • X-Forwarded-For

最小可用配置建议

OpenClaw Gateway

1
2
3
4
5
6
7
8
9
10
11
12
{
"gateway": {
"port": 18789,
"mode": "local",
"bind": "custom",
"customBindHost": "172.17.0.1",
"trustedProxies": ["172.18.0.0/16"],
"controlUi": {
"allowedOrigins": ["https://<YOUR_DOMAIN>"]
}
}
}

这里特别注意:

  • bindcustom
  • 不是 lan
  • 非 loopback 的浏览器访问场景,需要配置 controlUi.allowedOrigins

Caddyfile

1
2
3
4
5
6
7
8
9
<YOUR_DOMAIN> {
reverse_proxy https://172.17.0.1:18789 {
transport http {
tls_insecure_skip_verify
}

header_up X-Forwarded-For {remote_host}
}
}

这或许是更适合 OpenClaw 的“最小必要配置”。

如果改完配置还是会卡怎么办

如果你把反代和 Gateway 配置都补齐了,web_fetch 还是在某些页面卡住,那大概率就已经落到 OpenClaw 自身的已知问题上了。因为官方 issue 已经明确记录过:

  • web_fetch 对较大响应可能无限挂住
  • 即使 maxChars 设得很小也可能不返回

这时可以考虑两个 workaround:

1. 改用更轻的页面先测试

先测一个很小、很简单的页面,和 docs.openclaw.ai/tools 对比。

如果只有文档站容易挂,说明问题更偏向 web_fetch 自身对页面体量/提取流程的处理,而不是你的整个反代坏了。

2. 配 Firecrawl fallback

OpenClaw 官方文档写得很明确:Firecrawl 可以作为 web_fetch 的 fallback extractor,适合 JS-heavy 页面,或者普通抓取不顺的场景。

示例配置大概是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"tools": {
"web": {
"fetch": {
"firecrawl": {
"apiKey": "FIRECRAWL_API_KEY_HERE",
"baseUrl": "https://api.firecrawl.dev",
"onlyMainContent": true,
"maxAgeMs": 172800000,
"timeoutSeconds": 60
}
}
}
}
}

这不是免费方案,但确实是官方给出的解决方向之一。

最后的判断

如果你升级到 OpenClaw 2026.3.8 后,遇到:

  • dashboard 能打开
  • web_fetch 一直停在“正在获取…”
  • 使用了 Caddy 反向代理

建议优先按下面顺序排查:

  1. 先补 controlUi.allowedOrigins
  2. 确认 trustedProxies 写的是代理来源 IP 段,不是上游目标地址
  3. 确认 Caddy 到 Gateway 的实际上游地址
  4. 显式传 X-Forwarded-For
  5. 观察日志是否只有 tool start、没有 tool end
  6. 如果配置都没问题,再把怀疑重点放回 web_fetch 已知挂起问题本身

via

  • OpenClaw Web Tools 文档:web_fetch 是 HTTP fetch + readable extraction,默认启用,不执行 JavaScript。(OpenClaw)
  • OpenClaw Gateway Configuration Reference:bindtrustedProxiesallowRealIpFallbackcontrolUi.allowedOrigins。(OpenClaw)
  • OpenClaw Firecrawl 文档:可作为 web_fetch fallback extractor。(OpenClaw)
  • OpenClaw 2026.3.8 release notes:未见 web_fetch 相关改动说明。(GitHub)
  • OpenClaw web_fetch 已知问题:较大页面响应可能无限挂起。(GitHub)