Skip to content

normalizeCdpWsUrl fails to rewrite ws://0.0.0.0 from containerized browsers #17752

@joeharouni

Description

@joeharouni

Description

When using a remote/containerized browser (e.g., browserless via Docker), Chrome reports webSocketDebuggerUrl: "ws://0.0.0.0:3000" in its /json/version response. normalizeCdpWsUrl() is supposed to rewrite the WebSocket URL to the external host:port from the configured cdpUrl, but it only does this when isLoopbackHost(ws.hostname) returns true.

0.0.0.0 (and ::) are not considered loopback by isLoopbackAddress() — this is technically correct for bind semantics, but wrong for CDP URL rewriting. These are wildcard bind addresses that mean "all interfaces inside the container," which has the same semantic intent as loopback: "this is my internal address, rewrite it."

Steps to Reproduce

  1. Run browserless (or any headless Chrome) in a Docker container with port mapping (e.g., 18850:3000)
  2. Configure OpenClaw with a remote browser profile:
    {
      "browser": {
        "defaultProfile": "browserless",
        "profiles": {
          "browserless": {
            "cdpUrl": "http://192.168.1.202:18850?token=...",
            "color": "#FF6600"
          }
        }
      }
    }
  3. Try to use the browser tool (screenshot, navigate, etc.)

Expected Behavior

normalizeCdpWsUrl("ws://0.0.0.0:3000", "http://192.168.1.202:18850?token=...") should rewrite to ws://192.168.1.202:18850?token=... — the same behavior as if Chrome had reported ws://127.0.0.1:3000.

Actual Behavior

The function sees 0.0.0.0 is not loopback, skips the rewrite, and OpenClaw tries to connect to ws://0.0.0.0:3000 literally. This fails, and the browser service reports "port is in use but not by openclaw."

Root Cause

In src/browser/cdp.ts, normalizeCdpWsUrl():

if (isLoopbackHost(ws.hostname) && !isLoopbackHost(cdp.hostname)) {
    ws.hostname = cdp.hostname;
    // ...
}

And in src/gateway/net.ts, isLoopbackAddress() explicitly excludes 0.0.0.0:

// 0.0.0.0 and :: are NOT loopback

Proposed Fix

Treat 0.0.0.0 and :: as wildcard addresses that need rewriting, alongside loopback:

const isWildcard = ws.hostname === "0.0.0.0" || ws.hostname === "::";
if ((isLoopbackHost(ws.hostname) || isWildcard) && !isLoopbackHost(cdp.hostname)) {

This doesn't change isLoopbackAddress (which is correct for its general purpose) — it only adds wildcard handling to the CDP URL rewrite logic where the intent is clear.

Environment

  • OpenClaw v2026.2.14
  • browserless/chromium 2.38.4 (Chrome 145)
  • Docker 28.1.1, Debian 12

Metadata

Metadata

Assignees

No one assigned

    Labels

    staleMarked as stale due to inactivity

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions