Puppeteer WebSocket Connection Options Explained
Connect Puppeteer to a running browser, pass WebSocket headers, choose protocol options, and detach without shutting Chrome down.
puppeteer.connect() attaches Puppeteer to an already-running browser and returns a Browser instance. If you already have the complete Chrome DevTools WebSocket URL, pass it as browserWSEndpoint. In Node.js, put WebSocket headers such as an authorization header under wsOptions.headers. Use browser.disconnect() to detach while leaving the browser and its pages running; use browser.close() when you intend to shut the browser down.
This guide follows the Puppeteer 25.12.0 API reference. The browser-management guide linked below is under Puppeteer’s /next/ documentation path.
1. Connect to an existing browser with a WebSocket URL
Install Puppeteer in a Node.js project if it is not already available:
npm install puppeteer
Then connect using the full debugger WebSocket endpoint supplied by the browser or your browser-management setup:
const puppeteer = require('puppeteer');
async function main() {
const endpoint = process.env.PUPPETEER_WS_ENDPOINT;
if (!endpoint) {
throw new Error('Set PUPPETEER_WS_ENDPOINT to the browser WebSocket URL');
}
const browser = await puppeteer.connect({
browserWSEndpoint: endpoint,
});
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 stays running.
browser.disconnect();
}
}
main().catch(error => {
console.error(error);
process.exitCode = 1;
});
connect() attaches; it does not launch a browser. The official connect() API reference describes it as connecting Puppeteer to an existing browser. The current ConnectOptions reference lists both browserURL and browserWSEndpoint, but its generated rows do not describe their behavior. This guide uses browserWSEndpoint when a complete debugger WebSocket URL is available and does not assume undocumented discovery or precedence rules for browserURL.
2. Find the browser WebSocket endpoint
When Puppeteer itself launched the browser, get the endpoint from browser.wsEndpoint(). The Browser.wsEndpoint() API reference documents the method and shows that the value can be saved, disconnected from, and used later to reconnect.
const puppeteer = require('puppeteer');
async function main() {
const browser = await puppeteer.launch();
const endpoint = browser.wsEndpoint();
console.log(endpoint);
// A different client or later process can connect with:
// const attached = await puppeteer.connect({ browserWSEndpoint: endpoint });
await browser.close();
}
main().catch(console.error);
For a running Chrome debugger endpoint, Puppeteer documents the webSocketDebuggerUrl field at http://HOST:PORT/json/version. The documented WebSocket form is ws://HOST:PORT/devtools/browser/<id>. Treat the endpoint and any credentials as sensitive: do not publish them in logs, source control, or a public page. See the endpoint API documentation.
curl http://127.0.0.1:9222/json/version
Read the webSocketDebuggerUrl value from the JSON response and pass that complete value to browserWSEndpoint. Availability of that host and port depends on how the browser was started or managed. Do not assume that a cloud browser service uses this host, port, path, or authentication method; check that provider’s own current instructions.
3. Add WebSocket headers in Node.js
For Node.js, current WebSocket configuration belongs in wsOptions. To send a header during connection, put it under wsOptions.headers:
const puppeteer = require('puppeteer');
async function main() {
const endpoint = process.env.PUPPETEER_WS_ENDPOINT;
const token = process.env.BROWSER_TOKEN;
if (!endpoint || !token) {
throw new Error('Set PUPPETEER_WS_ENDPOINT and BROWSER_TOKEN');
}
const browser = await puppeteer.connect({
browserWSEndpoint: endpoint,
wsOptions: {
headers: {
Authorization: `Bearer ${token}`,
},
},
});
try {
const page = await browser.newPage();
await page.goto('https://example.com');
console.log(await page.title());
} finally {
browser.disconnect();
}
}
main().catch(error => {
console.error(error);
process.exitCode = 1;
});
This illustrates Puppeteer’s option shape; use the header name and credential format required by your browser endpoint. There is no universal authentication convention for remote-browser services. The top-level headers option is deprecated; the API reference says wsOptions.headers takes precedence if both are present. Refer to the ConnectOptions reference.
wsOptions is Node-only. The browser build can connect to an existing browser over WebSockets, but browser-side WebSocket options such as Node keep-alive settings are not available there; Puppeteer also documents that browser builds do not have a ping-frame API. Do not rely on keep-alive options in a browser build.
4. Choose protocol and session behavior
Connection options configure different layers. The endpoint identifies the browser; wsOptions controls Node WebSocket behavior; protocol, timeout, viewport, and target options affect Puppeteer’s session behavior.
| Option | What it controls | When to use it |
|---|---|---|
protocol |
Protocol used for the connection. The reference documents CDP as the runtime default for a browser connection. | Set deliberately when using a supported non-default protocol. Chrome launch defaults to CDP; Firefox launch defaults to WebDriver BiDi. |
capabilities |
Capabilities for protocol: 'webDriverBiDi' with connect(). |
When connecting with WebDriver BiDi and you need to request capabilities. |
protocolTimeout |
Timeout for an individual protocol call; documented default is 180000 ms. |
Adjust when protocol operations need a different limit. It is not documented as the WebSocket handshake timeout. |
defaultViewport |
Viewport applied to each page; documented default is { width: 800, height: 600 }. |
Set a consistent page viewport for automation. |
targetFilter |
A callback that decides which browser targets Puppeteer connects to. | When your automation needs to filter connected targets. |
slowMo |
Delays Puppeteer operations by the specified number of milliseconds. | Debugging flows where slower actions are easier to observe. |
networkEnabled |
Experimental network-event monitoring control. Setting it to false disables monitoring. |
Only when you understand that features using HTTPRequest and HTTPResponse events can break. |
allowlist / blocklist |
Experimental Chrome-only URLPattern controls, documented for Chrome 149 or later. | Use for the documented target and request controls, not as a complete network sandbox. Disallowed requests fail and existing targets that violate the rules may be detached. |
channel |
Experimental Node.js Chrome option that looks for an open Chrome WebSocket in a known user-data directory and connects to its active port. | Only when this Chrome-specific discovery behavior suits the setup; otherwise provide the endpoint explicitly. |
For exact types and any changes in later releases, consult the versioned ConnectOptions API reference. Puppeteer warns that allowlist and blocklist are not complete network sandboxing; use container or operating-system controls when you need that stronger isolation.
5. Disconnect or close the browser
Choose lifecycle behavior based on who owns the browser process:
browser.disconnect()detaches the Puppeteer client. Puppeteer’s Browser management guide says: “Unlikebrowser.close(),browser.disconnect()does not shut down the browser or close any pages.”browser.close()gracefully closes the browser. Use it when this code owns the browser and should end its process; the guide says: “To gracefully close the browser, you use thebrowser.close()method.”
For reconnect flows, preserve browser.wsEndpoint(), disconnect, then call puppeteer.connect({ browserWSEndpoint }) while the browser is still running. The wsEndpoint reference includes this pattern.
6. Run Puppeteer from a browser page
Puppeteer’s browser build can connect over WebSockets to an existing browser, but it cannot launch or download browsers because those operations depend on Node.js APIs. The official Running Puppeteer in the browser guide demonstrates the browser-specific puppeteer-core entry point and a browserWSEndpoint connection.
// In a browser build configured with the Puppeteer browser entry point:
const browser = await puppeteer.connect({
browserWSEndpoint: 'ws://HOST:PORT/devtools/browser/ID',
});
const page = await browser.newPage();
await page.goto('https://example.com');
console.log(await page.title());
browser.disconnect();
The endpoint above is a placeholder. A browser page also has to be permitted by its security and network environment to reach the WebSocket endpoint. Do not expose a privileged browser debugger endpoint to an untrusted web page.
7. Transport boundaries: WebSocket versus pipe
A Chrome pipe is a launch-time transport option documented in LaunchOptions. It is not an endpoint to pass to connect() for attaching to an already-running browser. For an existing browser, use a documented connection endpoint such as the browser WebSocket URL.
8. Troubleshoot connection failures
| Symptom | Likely cause | What to check |
|---|---|---|
| Connection refused or timeout | The host/port is unreachable, the browser is not listening, or network routing blocks the connection. | Confirm the browser is running and that the endpoint host and port are reachable from the Node process. Check the browser’s /json/version response where applicable. |
| Invalid endpoint or WebSocket handshake failure | A discovery HTTP URL, incomplete WebSocket URL, stale endpoint, or provider-specific URL was supplied. | Use the complete webSocketDebuggerUrl for a debugger endpoint. Check the endpoint source and lifetime with its owner/provider. |
| HTTP 401 or 403 during connection | Required authentication is missing, incorrect, or sent in the wrong form. | Confirm the provider’s exact header or credential requirements. Put Node WebSocket headers in wsOptions.headers; Puppeteer does not define a universal provider auth scheme. |
| Header appears to be ignored | It may be configured in deprecated top-level headers, or connection runs in a browser build. |
Use wsOptions.headers in Node.js. Browser builds do not support Node wsOptions. |
| A protocol operation times out | An individual CDP or protocol call exceeded protocolTimeout. |
Distinguish the protocol-call timeout from network reachability and handshake issues. Increase the option only if the operation legitimately needs longer. |
| Page/request events are missing | networkEnabled may be false. |
Enable network monitoring if code depends on HTTPRequest or HTTPResponse events. |
| Browser closes unexpectedly after automation | The code called browser.close() even though another process owns the browser. |
Use browser.disconnect() to detach and let the owner manage shutdown. |
| Connection works locally but not from a browser page | The browser page cannot reach the endpoint or its environment blocks the WebSocket connection. | Check network reachability and browser security constraints; use a server-side Node process if the endpoint should not be exposed to page code. |
9. Performance, reliability, and cost considerations
- Timeouts:
protocolTimeoutgoverns individual protocol calls, not documented WebSocket discovery or handshake time. Diagnose network reachability separately. - Session overhead: Reuse an attached browser when your process is designed to share its lifecycle. Create and close pages deliberately, and detach with
disconnect()when the browser belongs to another manager. - Reliability: An endpoint can stop working if its browser process exits or its owner rotates the endpoint. The Puppeteer API does not establish endpoint lifetime for third-party services; check the service’s documentation and reconnect only after confirming a fresh endpoint.
- Network controls: Experimental allow/block lists do not replace OS or container sandboxing. Keep browser debugging endpoints private and protect credentials.
- Cost: Puppeteer is the automation library; browser hosting and infrastructure costs depend on how you run or obtain the browser. Puppeteer’s generic API reference does not specify provider pricing.
Or skip the browser setup
If your goal is a website screenshot rather than browser automation, ScreenshotNeo is a website screenshot API and MCP server. Its one-call API returns a screenshot or PDF; 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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', image));
- Cookie banners are accepted and removed before the shot, and known newsletter popups and chat widgets are removed.
- Bot checks, blank pages, failed loads, timeouts, and cache hits are never billed; response headers identify the page verdict and billing status.
- An MCP server lets AI agents using Claude, Cursor, or another MCP client take screenshots with
take_screenshot, inspect pages withget_page_info, and create PDFs withcapture_pdf. - The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up for 1,000 free screenshots a month, with no card.
FAQ
What is the difference between browserURL and browserWSEndpoint?
The current generated ConnectOptions table lists both but leaves their descriptions blank. Use browserWSEndpoint when you have the full debugger WebSocket URL; consult current Puppeteer documentation for any additional locator behavior rather than assuming discovery rules.
How do I authenticate a remote browser connection?
Use the remote-browser service’s documented authentication method. In Node.js, Puppeteer’s transport headers are configured with wsOptions.headers, but header names and token formats vary by service.
Does browser.disconnect() close Chrome?
No. It detaches Puppeteer and leaves the browser and its pages running. Use browser.close() to gracefully close the browser.
Can I connect to a browser over a pipe?
The documented Chrome pipe option is for launching a browser. It is not the transport endpoint for connect() to an already-running browser.


