How to Connect Puppeteer to an Existing Browser
Connect Puppeteer to a running browser through its Chrome DevTools WebSocket endpoint. Find the endpoint, use it safely, and troubleshoot common connection issues.
To connect Puppeteer to a browser that is already running, retrieve that browser’s Chrome DevTools Protocol (CDP) WebSocket endpoint and pass it to puppeteer.connect(). The endpoint is usually available as webSocketDebuggerUrl from http://HOST:PORT/json/version. Puppeteer then returns a Browser object you can use to open pages or work with existing ones.
This guide uses Node.js, because Puppeteer’s connection API is a Node API. The browser must already expose a debugging endpoint that the Node process can reach. See the official Puppeteer connect API and browser management guide.
1. Get the browser’s WebSocket endpoint
Open http://HOST:PORT/json/version from a machine that can reach the browser’s debugging server. Replace HOST and PORT with the address and port configured for that browser. The response includes a webSocketDebuggerUrl field, typically shaped like ws://HOST:PORT/devtools/browser/<id>.
curl http://127.0.0.1:9222/json/version
Example response (the browser ID is specific to that running instance):
{
"Browser": "Chrome/…",
"Protocol-Version": "1.3",
"webSocketDebuggerUrl": "ws://127.0.0.1:9222/devtools/browser/<id>"
}
Use the returned URL as-is. Do not guess the browser ID or substitute a page-level WebSocket endpoint. Puppeteer documents the browser endpoint format as ws://HOST:PORT/devtools/browser/<id>; its Browser.wsEndpoint() reference describes it as the URL used to connect to that browser.
2. Connect with Puppeteer
Install Puppeteer in the Node.js project that will make the connection. The browser itself must be running separately and reachable at the endpoint you discovered.
npm install puppeteer
Save the following as connect.mjs. Set BROWSER_WS_ENDPOINT to the exact webSocketDebuggerUrl from /json/version, then run node connect.mjs.
import puppeteer from 'puppeteer';
const endpoint = process.env.BROWSER_WS_ENDPOINT;
if (!endpoint) {
throw new Error('Set BROWSER_WS_ENDPOINT to the browser webSocketDebuggerUrl');
}
const browser = await puppeteer.connect({
browserWSEndpoint: endpoint,
});
try {
console.log('Connected to:', await browser.version());
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
console.log('Page title:', await page.title());
} finally {
// Detach Puppeteer while leaving the browser process running.
browser.disconnect();
}
Run it with the endpoint in the environment:
BROWSER_WS_ENDPOINT='ws://127.0.0.1:9222/devtools/browser/<id>' node connect.mjs
The value returned by puppeteer.connect() is a Browser. You can call browser.newPage() to create a page, or browser.pages() to inspect pages already open in that browser. For API details, use the official connect reference.
3. Choose the right connection and cleanup behavior
browserWSEndpoint: the direct WebSocket route
Use browserWSEndpoint when you have the debugger WebSocket URL from /json/version. This is the explicit standard recipe documented by Puppeteer. The connection protocol is determined at runtime and defaults to CDP when connecting to a browser.
browserURL: an available connection option
Puppeteer’s ConnectOptions reference also lists browserURL. The research available for this guide confirms that it is an option, but does not establish enough detail to give a reliable URL format or its exact use cases. When you have the documented WebSocket URL, prefer the explicit browserWSEndpoint flow above.
disconnect() or close()
| Call | Effect | Use it when |
|---|---|---|
browser.disconnect() |
Detaches Puppeteer; the browser process and its pages keep running. | The browser is shared, persistent, or managed by another process. |
browser.close() |
Closes the browser. | Your code owns the browser lifecycle and intends to shut it down. |
For a browser started or managed elsewhere, disconnect() is usually the appropriate cleanup. Use close() only when the caller should end the browser session. Puppeteer documents these lifecycle operations in its browser management guide.
Separate state with browser contexts
If tasks should not share cookies or local storage, create separate browser contexts. Puppeteer’s browser management guide describes contexts as storage-isolated: cookies and local storage are not shared between contexts. This is useful when a connected browser serves multiple independent jobs. Context support and available operations can depend on the connected browser, so handle runtime errors if you use context features with a remote or managed browser.
Protocol and runtime limits
- CDP: the default protocol when connecting to a browser.
- WebDriver BiDi: an explicit protocol case configured with
protocol: 'webDriverBiDi'; do not assume CDP-specific behavior will apply unchanged. channel: marked experimental forconnect(). The reference says it looks for an open WebSocket in the channel’s well-known default user data directory and works only for Chrome in Node.js. Treat it as a special case, not the baseline connection method.- Browser page runtime: a browser-compatible Puppeteer build can connect over WebSockets to an existing browser, but cannot launch or download a browser there because those actions depend on Node.js APIs. The official guide points to the browser-specific
puppeteer-coreentry point.
These documented behaviors do not guarantee compatibility with every Chrome, Chromium, or remote-browser build. Confirm the endpoint is reachable and that the browser exposes the expected protocol.
4. Or skip the browser setup
If your goal is a screenshot rather than browser automation, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns an image or PDF, so you do not need to manage a browser process or debugger endpoint for a capture. 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
ScreenshotNeo accepts cookie or consent banners as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots, and every feature is on every plan.
Sign up for 1,000 free screenshots a month, no card required.
5. Troubleshooting connection failures
| Symptom | Likely cause | What to check or change |
|---|---|---|
| Connection refused or timeout | The browser is not running, the host or port is wrong, or the debugging endpoint is not reachable from the Node process. | Request http://HOST:PORT/json/version from the same machine or network namespace as Node. Confirm the browser is running and the configured port is reachable. |
/json/version returns an error or not found |
You reached the wrong host or port, or that browser has not exposed its debugging endpoint. | Check the browser service configuration and address. The connection recipe requires an endpoint the browser actually exposes. |
| WebSocket handshake fails | The endpoint is malformed, stale, or not the browser-level debugger URL. | Fetch webSocketDebuggerUrl again from /json/version and pass that exact value as browserWSEndpoint. A browser restart can change the instance-specific ID. |
| Connected, but expected tabs are missing | The code is inspecting a different browser instance, or the desired pages have not been opened there. | Log await browser.pages() and verify that the endpoint belongs to the intended running browser. |
| Browser exits after the script finishes | The cleanup path called browser.close(), which closes the browser. |
Use browser.disconnect() when Puppeteer should detach and leave the browser and its pages running. |
| Launch or download fails in a browser page runtime | Launching and downloading depend on Node.js APIs and are not supported in that runtime. | Connect to an existing browser over WebSockets, or run the Node.js version of Puppeteer where launching is needed. |
| CDP-specific operations fail with a BiDi connection | The connection was configured for WebDriver BiDi, which has different protocol capabilities. | Check the configured protocol and use an operation supported by that protocol, or connect with the default CDP behavior when that is what the browser exposes. |
6. Performance, reliability, and cost considerations
- Endpoint reachability is the first reliability check. Retrieve the endpoint from the browser instance you intend to control and test
/json/versionfrom the same environment as the Puppeteer process. - Expect instance-specific endpoints. The browser WebSocket URL includes an ID. Discover it at runtime instead of storing an old value across browser restarts.
- Use explicit ownership cleanup. Disconnecting avoids shutting down a browser owned by another process. Closing ends the browser and should be deliberate.
- Isolate concurrent jobs when needed. Separate contexts keep cookies and local storage apart, according to Puppeteer’s browser management guide.
- Do not infer speed or capacity from the API shape. The cited Puppeteer documentation establishes how to connect and manage the browser, but does not provide a universal latency, throughput, or resource benchmark. Browser load and remote network conditions affect the result.
- Account for the browser owner’s resources. Connecting does not make the browser free to run: pages, downloads, and other browser work consume resources on the host that owns it. Set workload limits in the surrounding system based on its actual capacity.
For version-specific details, check Puppeteer’s current connect API and ConnectOptions reference. The documentation inspected for this guide displayed Puppeteer 25.12.0; API details, especially experimental options, may change.
7. Frequently asked questions
Can I connect to a browser launched by another program?
Yes, if it exposes a debugger WebSocket endpoint and that endpoint is reachable by the process running Puppeteer. Retrieve the URL from that browser’s /json/version response.
Does connecting download or start a browser?
No. puppeteer.connect() attaches to an existing browser. Launching or downloading one is a separate workflow, and browser-page runtimes cannot perform those Node.js-dependent actions.
Will disconnect() close my open tabs?
No. It detaches Puppeteer and leaves the browser process and its pages running. Use close() when you intend to close the browser.
Can multiple tasks use the same browser?
They can connect to a browser endpoint, but tasks that need isolated cookies and local storage should use separate browser contexts. Coordinate access to shared pages and browser lifecycle in the application that owns the browser.


