如果你正在使用 OpenClaw 构建 AI 自动化工作流,你一定会用到它的核心技能之一:网页控制(Browser Automation)。通过 Chrome DevTools Protocol (CDP),OpenClaw 可以像真人一样浏览网页、抓取快照、点击按钮和填写复杂的表单。
然而,如果你像我一样,选择在 Windows 的 WSL (Windows Subsystem for Linux) 环境下使用 Docker 部署 OpenClaw,你很快就会撞上一堵"网络隔离"的高墙。
这篇文章将带你还原这个经典的踩坑过程,并给出最终的正确架构方案:如何安全地让 Docker 里的 OpenClaw 接管宿主机的浏览器,并保持持久化的登录状态。
1. 踩坑实录:看似在同一栋楼,实则隔了一座山
最初的设想很简单:在宿主机启动一个开启了 9222 调试端口的 Chrome,然后让 Docker 容器里的 OpenClaw 去连接它。
但在 Docker Desktop on Windows 环境下,你会发现无论怎么配置,连接总是超时失败:
- ❌
localhost:9222无效: 容器里的 localhost 只是容器自己,找不到外面的浏览器。 - ❌ Docker 网关 IP(如
172.18.0.1)无效: 它只是 NAT 网关,路由不到 WSL 内部。 - ❌ WSL 局域网 IP(如
192.168.5.x)无效: Docker Desktop 使用的是 Hyper-V 虚拟交换机,而 WSL2 有自己独立的虚拟网络。两者虽然都在一台物理机上,但在网络层面上互不相通。
由于原生网络路由走不通,我们需要引入一个轻量级的内网穿透工具来搭桥。
2. 解决方案设计:Cloudflare Tunnel + 状态持久化
目前的最佳实践方案是使用 Cloudflared Tunnel 将宿主机的调试端口暴露出来,让容器通过公网(或安全隧道)绕回宿主机。
但在打通网络之外,还有一个极其重要的业务痛点:防拦截与登录状态复用。
假设你的 OpenClaw 技能链条非常长------比如它需要先去竞争对手的电商网站定时抓取特定商品的价格和库存,然后自动登录你的内部后台系统(如 Jira 或 Notion)生成数据报告,最后将关键的降价预警通过网页版 WhatsApp 或 Slack 自动发送给销售团队。
如果你每次连接的都是一个纯净无痕的 Headless 浏览器,AI 就会反复卡在扫码和登录验证码上,自动化就成了空谈。因此,我们必须让被接管的浏览器记住你是谁。
3. 核心配置步骤
以下是实现"网络穿透 + 状态复用"的完整操作指南:
第一步:在 WSL 中启动带"用户数据"的无头浏览器
这是最关键的一步。除了开启远程调试和无头模式,你必须 加上 --user-data-dir 参数,为 OpenClaw 分配一个专属的本地文件夹来持久化保存 Cookie 和 Session。
打开 WSL 终端,执行以下命令:
google-chrome --remote-debugging-port=9222 --remote-debugging-address=0.0.0.0 --headless=new --disable-gpu --no-sandbox --user-data-dir="$HOME/openclaw-chrome-data"
(💡 技巧:第一次运行时,你可以先去掉 --headless=new 参数,手动在弹出的界面里把需要的平台账号全部登录好,然后再切回无头模式后台运行。)
第二步:启动 Cloudflared 内网穿透
保持浏览器运行,在另一个终端窗口中启动 Tunnel,将本机的 9222 端口暴露出去:
cloudflared tunnel --url http://127.0.0.1:9222
稍等片刻,终端会输出一个类似 https://your-random-words.trycloudflare.com 的公网地址。

第三步:配置 OpenClaw 连接隧道
由于 CDP 协议走的是 WebSocket,而且浏览器底层控制不仅需要域名端口,还需要具体的浏览器实例 UUID。
你需要请求本地的 http://127.0.0.1:9222/json/version 获取类似 /devtools/browser/xxxx-xxxx... 的路径,并将其与 Cloudflare 生成的域名拼接,将 https 替换为 wss。最终填入 OpenClaw 配置文件的地址应该长这样:

{
"browser": {
"enabled": true,
"driver": "chrome-devtools-mcp",
"mcp": {
"cdpUrl": "wss://your-random-words.trycloudflare.com/devtools/browser/1234abcd-..."
}
}
}
重启你的 OpenClaw 容器。现在,你可以直接在聊天框发送指令:"打开微信公众平台"。

4. ⚠️ 必读的安全警告
由于 CDP(Chrome DevTools Protocol)本身没有任何密码校验机制,通过 Tunnel 将 9222 端口暴露在公网上,意味着任何拿到这个 URL 的人都可以直接获取你这台机器的执行权限。
最佳实践: 自动化任务执行完毕后,务必及时关掉 Cloudflared Tunnel。千万不要让它一直在后台运行。
5. 附赠:一键安全启停与 URL 自动抓取脚本 (Bash Script)
为了避免每次都要开两个终端窗口分别启动 Chrome 和 Cloudflared,且容易忘记关闭带来安全隐患,我写了一个终极版的 Bash 脚本。
这个脚本将两个进程绑定在一起,实现一键启动 ,它会自动轮询获取 Cloudflare 域名 ,并自动提取 Chrome 底层实例的 UUID 进行完美拼接 。最后,在你按下 Ctrl+C 时它会自动安全销毁 Tunnel 进程,防止端口泄露。
在你的 WSL 中创建一个名为 openclaw-chrome-start.sh 的文件,填入以下代码:
#!/bin/bash
# openclaw-chrome-start.sh
# 一键启动 Chrome(带状态持久化)+ Cloudflare Tunnel
# 智能抓取并拼接完整的 WebSocket URL (带浏览器 ID)
CHROME_DATA_DIR="$HOME/openclaw-chrome-data"
echo -e "🚀 启动 \033[0;34mOpenClaw Chrome + Tunnel\033[0m..."
cleanup() {
echo -e "\n🛑 收到关闭信号,正在清理..."
pkill -f "cloudflared tunnel" 2>/dev/null
echo -e "✅ 已安全关闭 Cloudflared Tunnel,公网访问已切断。"
echo -e "💡 Chrome 继续在本地后台运行,下次连接速度更快。"
exit 0
}
trap cleanup SIGINT
# 1. 检查 Chrome 是否已运行
if pgrep -f "chrome.*remote-debugging" > /dev/null; then
echo "✅ Chrome 已在运行,复用已有实例"
else
echo "📱 启动 Chrome(数据目录: $CHROME_DATA_DIR)..."
google-chrome \
--remote-debugging-port=9222 \
--remote-debugging-address=0.0.0.0 \
--user-data-dir="$CHROME_DATA_DIR" \
--headless=new \
--no-sandbox \
--disable-gpu \
> /tmp/chrome-output.txt 2>&1 &
sleep 2
fi
# 2. 启动 Cloudflare Tunnel
echo "🌍 正在启动 Cloudflared Tunnel..."
> /tmp/tunnel-output.txt
cloudflared tunnel --url http://127.0.0.1:9222 > /tmp/tunnel-output.txt 2>&1 &
TUNNEL_PID=$!
echo "⏳ 正在与 Cloudflare 节点建立连接获取地址 (最多等待 15 秒)..."
# 3. 智能轮询获取 Tunnel URL
TUNNEL_URL=""
for i in {1..15}; do
TUNNEL_URL=$(grep -o 'https://[-a-zA-Z0-9]*\.trycloudflare\.com' /tmp/tunnel-output.txt | head -n 1)
if [ -n "$TUNNEL_URL" ]; then
break
fi
sleep 1
done
if [ -z "$TUNNEL_URL" ]; then
echo -e "\n⚠️ 获取超时!未能自动提取到 Tunnel 地址,请手动查看日志:"
cat /tmp/tunnel-output.txt | grep -E "ERR|INF"
pkill -f "cloudflared tunnel" 2>/dev/null
exit 1
fi
# 4. 获取 Chrome DevTools 具体的 UUID 路径并拼接
echo "🔍 正在获取 Chrome 浏览器底层实例 ID..."
# 把 https 换成 wss
WSS_BASE="${TUNNEL_URL/https/wss}"
# 用 curl 获取 JSON 数据,并用 grep 正则提取 /devtools/browser/... 路径
WS_PATH=$(curl -s http://127.0.0.1:9222/json/version | grep -o "/devtools/browser/[a-f0-9-]*")
if [ -z "$WS_PATH" ]; then
echo -e "⚠️ 警告:无法获取 DevTools 路径,可能是 Chrome 启动异常。"
FINAL_URL="$WSS_BASE"
else
# 完美拼接
FINAL_URL="${WSS_BASE}${WS_PATH}"
fi
# 5. 输出最终结果
echo -e "\n======================================================="
echo -e "🎉 环境启动成功!"
echo -e "👉 请将以下【完整】地址复制到 OpenClaw 的配置中:"
echo -e "\033[1;32m$FINAL_URL\033[0m"
echo -e "=======================================================\n"
echo -e "🛑 保持此窗口打开。自动化执行完毕后,请按 \033[0;31mCtrl+C\033[0m 关闭 Tunnel。"
# 挂起脚本,等待用户按 Ctrl+C
wait $TUNNEL_PID
使用方法:
- 赋予脚本执行权限:
chmod +x openclaw-chrome-start.sh - 运行脚本:
./openclaw-chrome-start.sh - 复制终端绿色的完整 URL 即可使用。
- 测试完毕后,在这个终端窗口按下
Ctrl+C,脚本会自动杀掉 Tunnel 进程,确保系统安全,同时让 Chrome 在后台沉睡以备下次秒连。

希望这篇指南能帮你彻底解决 Docker 与宿主机浏览器通信的难题,让你的 AI Agent 真正做到跨平台的"手眼通天"!
本文由mdnice多平台发布