← Blog

BLOG

Puppeteer BrowserWSEndpoint Download: Connect to Remote Chromium

Learn what a Puppeteer browserWSEndpoint download actually installs, how to connect to remote Chromium, and when to move to a hosted runtime.

September 22, 20268 min readRemote Browser

# Puppeteer BrowserWSEndpoint Download: Connect to Remote Chromium

If you searched for a "puppeteer browserwsendpoint download," you are probably trying to connect Puppeteer to a Chromium instance that is not running on your laptop. The short answer: there is no separate browserWSEndpoint package to download. The browserWSEndpoint is a WebSocket URL that Puppeteer's puppeteer.connect() method consumes. What you download is Puppeteer itself, plus a browser binary if you run Chromium locally. When you connect to a remote runtime, you skip the binary download entirely and point Puppeteer at a hosted endpoint instead.

This guide explains what each piece does, how to wire up a connection, and the production criteria that decide whether you self-host Chromium or use a hosted runtime like Remote Browser.

What "browserWSEndpoint download" actually means

Puppeteer has two entry points:

  • puppeteer.launch() — starts a local Chromium process and manages its lifecycle.
  • puppeteer.connect({ browserWSEndpoint }) — attaches to an already-running browser over the Chrome DevTools Protocol (CDP).

The browserWSEndpoint is the WebSocket address of that running browser. It looks like this:

ws://127.0.0.1:9222/devtools/browser/6b1f0c3a-...

When you install Puppeteer via npm, you get the client library. Depending on your version and configuration, the install step may also fetch a matching Chromium build. That Chromium download is what most people conflate with a "browserWSEndpoint download." They are separate concerns:

ComponentWhat it isWhen you need it
puppeteer npm packageNode client libraryAlways
Chromium binaryLocal browser executableOnly for puppeteer.launch()
browserWSEndpointWebSocket URL to a running browserOnly for puppeteer.connect()
Remote runtimeHosted Chromium with a CDP endpointWhen you connect remotely

If you connect to a remote browser, you do not need a local Chromium binary at all. That is the key insight behind hosted runtimes: the browser lives somewhere else, and your code only needs the endpoint.

Why people look for a browserWSEndpoint download

There are a few common situations that lead to this search:

  1. You want to reuse a browser across scripts. Launching Chromium per script is slow. Connecting to a long-running browser via browserWSEndpoint is faster.
  2. You want to attach to a browser you already started. Maybe you launched Chrome with --remote-debugging-port=9222 and now need Puppeteer to drive it.
  3. You are moving automation off your machine. CI runners, containers, and cloud workers need a browser that is not tied to a developer laptop.
  4. You hit a "browserWSEndpoint not found" or connection error. This usually means the endpoint is wrong, the browser is not exposing CDP, or a proxy is blocking the WebSocket.

In all four cases, the fix is not a download. It is getting the correct endpoint and making sure the network path to it works.

How to connect Puppeteer to a remote browser

The connection pattern is the same whether the browser is on localhost or in a data center. You need three things: a reachable endpoint, the right protocol (ws or wss), and a client that speaks CDP.

Here is a minimal Puppeteer connection:

const puppeteer = require('puppeteer-core');

(async () => {
  const browser = await puppeteer.connect({
    browserWSEndpoint: process.env.BROWSER_WS_ENDPOINT,
    defaultViewport: null,
  });

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

  await browser.close();
})();

Note the use of puppeteer-core rather than puppeteer. The -core package does not download a Chromium binary, which is exactly what you want when connecting to a remote browser. This is the closest thing to a "browserWSEndpoint download" that makes sense: install the client, skip the binary.

If you are using Playwright instead, the equivalent is chromium.connectOverCDP(). The same endpoint works because both libraries speak CDP. Here is a TypeScript example that connects over CDP and reuses an existing context:

import { chromium, Browser, BrowserContext, Page } from 'playwright';

async function connectAndRun(endpoint: string): Promise<void> {
  const browser: Browser = await chromium.connectOverCDP(endpoint, {
    timeout: 30_000,
  });

  // A remote runtime may already have a context; reuse it if present.
  const context: BrowserContext =
    browser.contexts()[0] ?? (await browser.newContext());

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

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

  // Close the page, not the browser, if the runtime owns the session.
  await page.close();
}

connectAndRun(process.env.BROWSER_WS_ENDPOINT!).catch((err) => {
  console.error('CDP connection failed:', err);
  process.exit(1);
});

The Playwright CDP connection docs cover the connectOverCDP signature and its Chromium-only constraint. For the underlying protocol, the Chrome DevTools Protocol documentation describes the domains and events both libraries rely on.

Local Chromium vs. remote endpoint: the trade-offs

The decision is not about which is "better." It is about which failure modes you can tolerate.

CriterionLocal Chromium (launch)Remote endpoint (connect)
SetupDownload binary per machineOne endpoint, no binary
Startup latencyProcess spawn per runReuse a warm browser
ScalingBound by host CPU/RAMBound by runtime capacity
Session persistenceLost on process exitConfigurable profiles
Network/IP controlYour machine's IPRuntime proxy settings
DebuggingLocal DevToolsLive viewer or CDP
Failure isolationOne crash kills the runSession isolation per browser

Local Chromium is fine for development, one-off scripts, and small test suites. It becomes painful when you need many concurrent sessions, stable IPs, or sessions that survive a deploy. That is where a hosted runtime earns its place.

Production criteria for a remote browser runtime

If you are evaluating a hosted runtime to replace local Chromium, these are the questions that matter:

  • CDP compatibility. Does it expose a standard browserWSEndpoint that Puppeteer and Playwright can consume without a custom client? Anything that requires a proprietary SDK is a lock-in risk.
  • Session isolation. Are browsers isolated per session, or shared? Shared browsers leak cookies, storage, and state between tasks.
  • Persistent profiles. Can you keep cookies and logins across sessions? This matters for agents that need to stay authenticated.
  • Proxy and network controls. Can you route traffic through specific proxies, and are those settings configurable per session?
  • Observability. Is there a live viewer or a way to attach DevTools mid-run? Debugging a headless browser you cannot see is expensive.
  • Usage controls. Can you cap concurrent sessions and track browser-hours? Unbounded concurrency is a billing surprise waiting to happen.
  • Lifecycle semantics. Who closes the browser — your code or the runtime? Getting this wrong causes zombie sessions and leaked capacity.

Remote Browser addresses these with hosted Chromium sessions, CDP access, Playwright/Puppeteer/Selenium compatibility, a live viewer, persistent profiles, configurable browser settings, session isolation, and usage controls. Current limits and pricing are on the pricing page.

Common browserWSEndpoint errors and how to fix them

Most connection failures fall into a handful of categories.

`Error: connect ECONNREFUSED` — The endpoint host or port is wrong, or the browser is not listening. Verify the browser was started with --remote-debugging-port and that the port is reachable.

`WebSocket connection failed` behind a proxy — Corporate proxies often block ws://. Use wss:// if the runtime supports TLS, or configure the proxy to allow WebSocket upgrades.

`Protocol error (Target.attachToTarget)` — Version mismatch between the Puppeteer client and the remote Chromium. Pin your Puppeteer version to match the browser's CDP version, or use a runtime that tracks stable Chromium releases.

`browserWSEndpoint` returns but pages fail — The endpoint is valid but the session is already in use or has been closed by the runtime. Check session lifecycle rules.

Works locally, fails in CI — Usually a network egress rule. CI runners often have restricted outbound access. Allowlist the runtime's endpoint domain.

For a deeper look at running browsers without local setup, see Remote browser online: run real Chromium without managing Chrome.

When to move from local Chromium to a hosted runtime

The migration is worth it when any of these are true:

  • You run more than a handful of concurrent browser sessions.
  • Your automation needs to survive deploys, restarts, or autoscaling events.
  • You need consistent IPs or proxy routing that your local machine cannot provide.
  • You are building AI agents that need a browser runtime as infrastructure, not a script.
  • You spend more time debugging browser setup than writing automation logic.

The migration itself is small. Replace puppeteer.launch() with puppeteer.connect({ browserWSEndpoint }), point the endpoint at the runtime, and remove the local Chromium download from your build. Your page-level code does not change.

If you are building agents rather than scripts, the runtime layer matters more. See Remote browsers for AI agents: the missing runtime layer for how session isolation, profiles, and observability change agent reliability.

A practical migration checklist

  1. Audit your launch calls. Find every puppeteer.launch() and chromium.launch() in your codebase.
  2. Switch to `puppeteer-core` or `playwright-core`. Remove the bundled browser download from your dependencies.
  3. Introduce an endpoint config. Read BROWSER_WS_ENDPOINT from the environment so local and remote runs share code.
  4. Handle lifecycle explicitly. Decide whether your code or the runtime closes the browser. Do not assume.
  5. Add connection retries. Remote endpoints can be briefly unavailable during scaling. Retry with backoff.
  6. Test session isolation. Run two sessions in parallel and confirm they do not share cookies or storage.
  7. Instrument browser-hours. Track usage so you can forecast cost before it surprises you.

For the full API surface, including session creation and CDP details, see the documentation.

Where Remote Browser fits

Remote Browser is a browser API and runtime for AI agents and browser-use workflows. It provides hosted Chromium sessions with standard CDP endpoints, so Puppeteer, Playwright, and Selenium clients connect the same way they would to a local browser. There is no proprietary client to learn and no Chromium binary to download.

If you are connecting Puppeteer to a remote browser, the workflow is: get an endpoint, call puppeteer.connect(), and let the runtime handle the browser process. That is the whole point of a browserWSEndpoint — it decouples your automation code from the machine running the browser.

To see how this fits a broader automation stack, read Remote web browser: the practical runtime for browser automation or Remote control browser: when code and agents need to drive the web. When you are ready to run sessions, check pricing for current usage details.