← Blog

BLOG

Puppeteer BrowserWSEndpoint Chrome: Connect to Remote Chromium

Learn how puppeteer browserWSEndpoint connects Chrome over CDP, why it breaks, and how to run remote Chromium for AI agents reliably.

September 30, 20268 min readRemote Browser

# Puppeteer BrowserWSEndpoint Chrome: Connect to Remote Chromium

The browserWSEndpoint option is how Puppeteer connects to an already-running Chrome instance instead of launching its own. You pass a WebSocket URL, Puppeteer speaks the Chrome DevTools Protocol (CDP) over it, and your script drives a browser it never spawned. That single option is the difference between a local script and a remote browser runtime. This guide covers how browserWSEndpoint works, why it fails in practice, and how to wire it to hosted Chromium for AI agents and automation workloads.

If you are running agents rather than one-off scripts, the endpoint is the seam where local tooling meets production infrastructure. Getting it right determines whether your automation survives a laptop sleeping, a CI runner recycling, or a container restarting mid-task.

What browserWSEndpoint actually does

Puppeteer has two connection modes:

  • puppeteer.launch() spawns a Chrome process and manages its lifecycle.
  • puppeteer.connect({ browserWSEndpoint }) attaches to a Chrome that is already listening on a CDP WebSocket.

When Chrome starts with --remote-debugging-port=9222, it exposes an HTTP endpoint at http://localhost:9222/json/version. That response contains a webSocketDebuggerUrl field. That URL is your browserWSEndpoint. Puppeteer opens a WebSocket to it and issues CDP commands like Target.createTarget, Page.navigate, and Runtime.evaluate.

The important consequence: the browser process outlives your script. You can disconnect, reconnect, and keep the same tabs, cookies, and session state. That is exactly what you want for long-running agents, but it also means you now own the problem of keeping that browser alive and reachable.

The endpoint is not a download

A common confusion: people search for a "browserWSEndpoint download." There is nothing to download. The endpoint is a URL string produced by a running Chrome. What you install is Puppeteer itself (npm i puppeteer-core), and what you need is a Chrome instance already exposing CDP. If you are deciding between local and hosted, see Remote Browser Online for the trade-offs.

Connecting Puppeteer to remote Chrome over CDP

The minimal connection looks like this:

import puppeteer from 'puppeteer-core';

const browserWSEndpoint = process.env.BROWSER_WS_ENDPOINT!;

const browser = await puppeteer.connect({
  browserWSEndpoint,
  defaultViewport: null,
});

const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle2' });

const title = await page.title();
console.log({ title });

// Disconnect without killing the remote browser.
await browser.disconnect();

Two details matter in production:

  1. Use puppeteer-core, not puppeteer. The full package downloads a bundled Chromium you do not need when connecting remotely.
  2. Use browser.disconnect(), not browser.close(). close() sends Browser.close over CDP and terminates the remote browser. If the browser is shared or billed by session, that is a costly mistake.

If you are already using Playwright, the equivalent is chromium.connectOverCDP(endpoint), which speaks the same protocol. The Playwright CDP documentation confirms that connectOverCDP targets Chromium-based browsers, which is why Chrome and Chromium are the reliable targets for this pattern.

Why puppeteer browserWSEndpoint stops working

Most "puppeteer browserWSEndpoint not working" reports fall into a handful of categories. Diagnosing them is faster if you know what each failure looks like.

SymptomLikely causeFix
connect ECONNREFUSEDChrome not running or wrong portVerify --remote-debugging-port and that the process is alive
Unexpected server response: 404Endpoint path is stale or session expiredRe-fetch /json/version for a fresh webSocketDebuggerUrl
WebSocket closes immediatelySession was reaped or browser crashedAdd reconnect logic; check session lifetime limits
Works locally, fails in CIEndpoint bound to 127.0.0.1 onlyBind to 0.0.0.0 or use a hosted endpoint
Target closed mid-runAnother client called Browser.closeIsolate sessions; never share one endpoint across workers
Auth errors on connectMissing token or headerPass credentials via query string or headers per provider

The deeper issue is lifecycle. A locally launched Chrome with a debugging port is a single point of failure. If the machine reboots, the container is rescheduled, or the process is OOM-killed, the endpoint dies and every script holding it fails. For a one-off scrape that is fine. For an agent that runs for hours, it is not.

Endpoint stability is an infrastructure problem

A raw ws://localhost:9222/devtools/browser/<id> URL is not durable. The <id> changes every time Chrome restarts. Any config file, secret, or environment variable holding that URL goes stale. Production setups need a stable connection string that resolves to a live browser, plus a control plane that can create, pause, and destroy sessions on demand. That is the layer a hosted runtime provides.

Local Chrome vs hosted Chromium for Puppeteer

The decision is less about capability and more about operational cost. Both expose CDP; both accept browserWSEndpoint. What differs is who keeps the browser alive.

DimensionLocal Chrome + debug portHosted Chromium runtime
Endpoint stabilityChanges on every restartStable connection URL per session
LifecycleYou manage process, restarts, OOMManaged session with defined lifetime
ScalingOne machine, manual fan-outSessions created per task via API
State persistenceManual profile dirsPersistent profiles as a first-class feature
DebuggingLocal DevTools onlyLive viewer plus CDP access
Network identityYour IPConfigurable proxy and browser settings
Cost modelYour computeMetered per browser session

For a single developer running a nightly script, local Chrome is fine. For a team running concurrent agents against protected sites, the operational overhead of self-managed Chrome usually exceeds the cost of a managed runtime. The remote browser for AI agents write-up covers why the runtime layer exists at all.

Wiring a hosted endpoint into Puppeteer

With a hosted runtime, the flow is: request a session, receive a CDP endpoint, connect Puppeteer, run your task, release the session. The endpoint is the only thing your code needs to know.

import puppeteer from 'puppeteer-core';

async function runTask() {
  // Your runtime returns a CDP WebSocket URL for a fresh session.
  const { browserWSEndpoint, sessionId } = await createSession({
    profile: 'checkout-agent',
    proxy: 'residential-us',
  });

  const browser = await puppeteer.connect({
    browserWSEndpoint,
    defaultViewport: { width: 1280, height: 800 },
  });

  try {
    const page = await browser.newPage();
    await page.goto('https://example.com/login');
    await page.type('#email', process.env.AGENT_EMAIL!);
    await page.click('button[type=submit]');
    await page.waitForNavigation({ waitUntil: 'networkidle2' });
    return await page.title();
  } finally {
    await browser.disconnect();
    await releaseSession(sessionId);
  }
}

Key practices:

  • Always release sessions in a `finally` block. Leaked sessions cost money and consume capacity.
  • Never hardcode the endpoint. Fetch it per run so restarts and rebalances do not break you.
  • Separate profile from session. A persistent profile carries cookies and logins across sessions; the session is the ephemeral browser.
  • Use the live viewer during development. Watching a session in real time is faster than reading logs when a selector breaks.

For the broader pattern of driving a browser you do not own, Remote Control Browser walks through the control-plane model.

Where this fits for AI agents

Puppeteer is not only for deterministic scripts. Agent frameworks increasingly drive Chrome through CDP, and browserWSEndpoint is the standard handoff. The runtime requirements for agents differ from test suites in three ways:

  1. Longer sessions. An agent may hold a browser for minutes while a model reasons. Sessions must survive idle periods and reconnect cleanly.
  2. State that matters. Login state, cart contents, and multi-step form progress live in the profile. Losing it mid-task means restarting the task.
  3. Observability. When an agent fails, you need to see what it saw. A live viewer plus session recordings beats a stack trace.

This is why hosted Chromium is often paired with agent frameworks rather than raw Puppeteer. The endpoint is the same; the surrounding lifecycle management is what changes. If you are evaluating runtimes, Remote Web Browser compares the practical options.

A note on "free" cloud browsers

Searches for a free cloud browser for AI agents usually surface trial tiers or open-source projects you self-host. Both are legitimate starting points. The trade-off is that free tiers typically cap session duration or concurrency, and self-hosted options move the infrastructure problem back to you. Check current limits on the pricing page rather than assuming a specific allowance.

Production checklist for browserWSEndpoint

Before you ship a Puppeteer workload against a remote endpoint, verify:

  • Reconnect logic exists. Treat the WebSocket as unreliable. On disconnect, re-fetch the endpoint and resume if the session is still alive.
  • `disconnect()` is used, not `close()`. Unless you intend to terminate the browser.
  • Sessions are released. Wrap runs in try/finally and release on every exit path.
  • Profiles are named and versioned. A profile is state; treat changes to it like schema changes.
  • Timeouts are explicit. Set navigation and selector timeouts. Defaults are too generous for agents.
  • Proxy and browser settings are configured per task. Network identity affects success rates on protected sites.
  • You can watch a session live. Debugging blind is the most expensive habit in browser automation.

The documentation covers session creation, profile management, and CDP connection details for the runtime.

Summary

browserWSEndpoint is a small option with large operational implications. It lets Puppeteer attach to any Chrome exposing CDP, which means your automation is no longer bound to the machine that launched the browser. That is powerful for agents and fragile if you manage the browser yourself. The failure modes — stale endpoints, reaped sessions, shared browsers closed by another client — are all lifecycle problems, not protocol problems.

If you are running short-lived scripts, a local Chrome with a debug port is enough. If you are running agents that need persistent profiles, stable endpoints, live debugging, and configurable network identity, point browserWSEndpoint at a hosted Chromium runtime and let the control plane handle the rest. Start with the documentation to see how sessions and endpoints are issued, and check pricing for current usage details.