#15250·puppeteer

getWSEndpoint drops browserURL path prefix when resolving /json/version

Author: ren2019Created Jul 21, 2026Updated Jul 21, 2026

Bug description

getWSEndpoint() in BrowserConnector.ts uses new URL("/json/version", browserURL) to discover the WebSocket endpoint. When browserURL includes a path prefix (e.g., behind a reverse proxy or tunnel using URLs like http://host:port/t/<token>), the absolute path /json/version replaces the entire pathname of the base URL, silently dropping the prefix.

Steps to reproduce

  1. Run Chrome DevTools behind a reverse proxy that adds a path prefix, e.g. http://127.0.0.1:9222/t/<token>
  2. Pass this as browserURL to puppeteer.connect():
    puppeteer.connect({ browserURL: "http://127.0.0.1:9222/t/abc123" })
    
  3. HTTP request goes to http://127.0.0.1:9222/json/version instead of http://127.0.0.1:9222/t/abc123/json/version

Expected vs Actual

browserURL Expected Actual (broken)
http://host:9222/t/token /t/token/json/version /json/version
http://host:9222 /json/version /json/version

Root cause

const endpointURL = new URL("/json/version", browserURL);

Per WHATWG URL Standard, an absolute path (starts with /) replaces the base URL pathname entirely.

Proposed fix

const endpointURL = new URL(browserURL);
endpointURL.pathname = endpointURL.pathname.replace(/\/?$/, "/") + "json/version";

This appends json/version to the existing path instead of replacing it.

Impact

Any scenario where Chrome DevTools is accessed through a reverse proxy, API gateway, or tunnel with path-based routing. Common in cloud IDEs, containerized dev environments, and CDP-over-HTTP tunneling services.

Also affects chrome-devtools-mcp which bundles Puppeteer and exposes --browserUrl.