ScreenshotNeo

BlogGuides

Puppeteer Connection: How Browser Connections Work

Learn how Puppeteer connects to an existing browser, where to find its WebSocket endpoint, and when to disconnect or close Chrome.

By the ScreenshotNeo team4 October 20267 min read

Puppeteer connects to an existing browser with puppeteer.connect(). Provide either the browser’s WebSocket endpoint in browserWSEndpoint or its debugging address in browserURL. The call resolves to a Browser object you can use to open pages and automate the browser. Calling browser.disconnect() detaches Puppeteer but leaves the browser process and its pages running.

The connection flow has three steps: start or locate the browser, get its endpoint, then connect. See the official Puppeteer browser management guide and ConnectOptions reference.

1. Start or locate the browser

Use puppeteer.launch() when Puppeteer should start and manage the browser. Use puppeteer.connect() when another process, container, service, or tool already started it. This guide covers attaching to that existing instance.

The browser must expose a debugging connection that your Node.js process can reach. The endpoint is specific to the browser process; a port or endpoint copied from another instance may connect to the wrong browser or fail. Do not assume that a particular port or browser ID is universal.

2. Get the browser WebSocket endpoint

The endpoint is a WebSocket URL, typically shaped like ws://HOST:PORT/devtools/browser/<id>. Use the value for the browser you intend to control.

When Puppeteer launched the browser

Ask the returned Browser object for its endpoint with browser.wsEndpoint(). The Puppeteer API describes this as the WebSocket URL used to connect to that browser. Store it if another process will reconnect later.

When another process launched the browser

Read the browser’s debugger metadata at http://HOST:PORT/json/version and use the webSocketDebuggerUrl field. The host and port must be reachable from the Puppeteer process. In containerized deployments, a loopback address inside one container does not refer to a different container.

3. Connect and use the browser

Install Puppeteer in a Node.js project, then put the real endpoint from your browser into the example:

npm install puppeteer
const puppeteer = require('puppeteer');

async function main() {
  const browserWSEndpoint = process.env.BROWSER_WS_ENDPOINT;
  if (!browserWSEndpoint) {
    throw new Error('Set BROWSER_WS_ENDPOINT to the browser WebSocket URL');
  }

  const browser = await puppeteer.connect({ browserWSEndpoint });
  try {
    const page = await browser.newPage();
    await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
    console.log(await page.title());
  } finally {
    // Detach this Puppeteer client. The browser process stays running.
    browser.disconnect();
  }
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Set BROWSER_WS_ENDPOINT to the endpoint obtained from the browser process or debugger metadata. Avoid placing a real endpoint in source control: it can grant control over the browser session.

Connection options

puppeteer.connect() accepts a ConnectOptions object and resolves to a Browser. The current reference documents these main connection-related options; defaults and experimental options can change between Puppeteer releases.

Option Purpose When to use it
browserWSEndpoint Connect using the browser’s WebSocket debugger URL. Use when you have the full endpoint, including its browser-specific path.
browserURL Connect using the browser’s debugging address. Use when you know the reachable host and port and want Puppeteer to discover the WebSocket URL.
protocol Select the browser protocol. The documented default when connecting is CDP. The reference also describes WebDriver BiDi support; check the version-specific docs before relying on it.
transport Provide a custom transport. Use only when your connection setup requires a transport implementation beyond the standard connection.
wsOptions Configure the WebSocket connection. Node.js-only; the reference includes WebSocket options such as headers. The older top-level headers option is deprecated in favor of wsOptions.headers.
protocolTimeout Set the protocol call timeout. Adjust for workloads where protocol operations legitimately need more time, while investigating slow or unresponsive browsers.
slowMo Add a delay between Puppeteer operations. Useful for observing automation during debugging; it slows execution.
targetFilter Filter browser targets exposed to the connection. Use only when you deliberately need to limit visible targets and understand the effect on your workflow.

Choose one endpoint method appropriate to your setup. See the live ConnectOptions API reference for exact types and version-specific behavior.

Choosing browserURL or browserWSEndpoint

browserWSEndpoint is explicit: you provide the complete WebSocket URL. It is the clearest option when the endpoint is already available, such as from /json/version or browser.wsEndpoint().

browserURL is useful when you have the browser’s debugging HTTP address and want Puppeteer to resolve the WebSocket endpoint. The debugging address must be accessible from the process running Puppeteer. If discovery fails, retrieve webSocketDebuggerUrl from /json/version and connect with browserWSEndpoint.

Disconnecting, reconnecting, and closing

These operations have different lifecycle effects:

Operation Effect Use it when
browser.disconnect() Disconnects the Puppeteer client. The browser process and open pages remain running. Your automation client is finished for now, but the browser belongs to another process or will be reused.
browser.close() Closes the browser. Your code owns the browser lifecycle and intends to end it.

To reconnect later, retain the endpoint while it remains valid, then call puppeteer.connect({ browserWSEndpoint }) again. An endpoint can be saved and reused as demonstrated in the Puppeteer API examples, but it identifies a particular running browser; it is not a permanent address after that browser exits.

Common errors and fixes

Symptom Likely cause Fix
Connection refused or timeout The browser is not running, the debugging endpoint is not exposed, or the host/port cannot be reached from Node.js. Confirm the browser process is alive and the debugging address is reachable from the same network namespace as Puppeteer.
Invalid WebSocket URL A debugging HTTP URL was passed as browserWSEndpoint, or the WebSocket path/ID is missing. Use the webSocketDebuggerUrl value from /json/version, or pass the debugging address as browserURL.
Cannot connect after browser restart The old endpoint belonged to the previous browser process. Read the new endpoint after restart; do not assume the prior browser ID remains valid.
Connected to an unexpected browser The host, port, forwarded port, or container address points to a different instance. Check the endpoint from that exact browser’s debugger metadata and verify the network route.
Authentication or handshake failure A proxy or browser service requires WebSocket headers or credentials not supplied to the connection. Check the service’s connection requirements and configure supported Node.js wsOptions if needed. Never log secret headers or endpoint tokens.
Pages disappear after cleanup The code called browser.close() when it intended only to detach. Use browser.disconnect() to leave the browser and pages running.
Protocol operation times out The browser is stalled, overloaded, or the operation exceeds the configured protocol timeout. Check browser health and page load behavior first; adjust protocolTimeout only when longer operations are expected.

Performance, reliability, and cost considerations

Connecting attaches to an existing browser; it does not make the browser itself faster. Startup time is avoided when the browser is already running, but your workload still depends on browser capacity, page complexity, network access, and the number of concurrent sessions. Reusing a browser may suit repeated tasks, while separate instances can provide stronger workload isolation at the cost of additional browser resources.

For reliability, treat the WebSocket endpoint as process-specific state. Discover or refresh it after browser restarts, detect connection failures, and reconnect only after confirming that the intended browser is available. Use disconnect() in cleanup when the browser lifecycle is managed elsewhere; use close() when this code owns that lifecycle. Keep endpoint URLs and connection credentials out of logs and public client code.

There is no universal cost or performance figure for this connection pattern. Costs depend on where and how the browser is hosted and the workload it runs. Puppeteer documents the APIs, not a fixed hosting price or benchmark.

Optional: Puppeteer in a webpage

Puppeteer can also run from a browser-compatible bundle in a webpage and connect over WebSockets to a separate browser with debugging enabled. In that environment, Puppeteer cannot launch or download the browser directly because those actions depend on Node.js APIs. See the official guide to running Puppeteer in the browser.

Or skip the browser setup

If your goal is a screenshot rather than browser automation, ScreenshotNeo provides a website screenshot API and MCP server. A single GET request returns a PNG, JPEG, WebP, or PDF. Its capture flow accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers report the page verdict and billing status. AI agents can use its MCP tools, including take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. See the ScreenshotNeo API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Create a free ScreenshotNeo account for 1,000 screenshots a month with no card.

FAQ

Does puppeteer.disconnect() close Chrome?

No. It detaches Puppeteer and leaves the browser process and pages running. Use browser.close() when you intend to close the browser.

Can I reconnect after disconnecting?

Yes, while the same browser is still running and its endpoint is reachable. Connect again with the saved WebSocket endpoint.

Where can I find webSocketDebuggerUrl?

Check the browser debugger metadata at http://HOST:PORT/json/version. The field contains the browser’s WebSocket endpoint.

Should I use launch() or connect()?

Use launch() when Puppeteer should start the browser. Use connect() to attach to a browser that is already running.