ScreenshotNeo

BlogHow-to

Disconnect from a Puppeteer Browser

Use `await browser.disconnect()` to detach Puppeteer while leaving the browser process and its pages running. Save the WebSocket endpoint first if you plan to reconnect.

By the ScreenshotNeo team4 October 20267 min read

Call await browser.disconnect() to detach Puppeteer from a browser without stopping the browser process or closing its pages. Use await browser.close() when you want to shut down the browser and its associated pages.

If you need to reconnect later, save browser.wsEndpoint() before disconnecting. You can then pass it to puppeteer.connect({ browserWSEndpoint }), provided that endpoint is still available to your process.

Disconnect without closing the browser

This complete example launches a browser, records its WebSocket endpoint, disconnects Puppeteer, reconnects, and finally closes the browser. It requires Node.js and the puppeteer package.

const puppeteer = require('puppeteer');

async function main() {
  const browser = await puppeteer.launch({ headless: true });
  const browserWSEndpoint = browser.wsEndpoint();

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

    // Detach this Puppeteer client. The browser and its pages remain open.
    await browser.disconnect();

    // Reattach using the endpoint saved before disconnecting.
    const browserAgain = await puppeteer.connect({ browserWSEndpoint });
    try {
      const pages = await browserAgain.pages();
      console.log('Pages after reconnect:', pages.length);
      console.log('Title after reconnect:', await pages[0].title());
    } finally {
      // This shuts down the browser and closes its associated pages.
      await browserAgain.close();
    }
  } catch (error) {
    // If the first connection is still attached, clean it up on failure.
    // A disconnected Browser object cannot be used to control the browser.
    try {
      await browser.close();
    } catch {
      // The browser may already be disconnected or closed.
    }
    throw error;
  }
}

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

Install Puppeteer with npm install puppeteer, save the example as disconnect.js, and run node disconnect.js. The example uses CommonJS; in an ES module, use import puppeteer from 'puppeteer'; instead. The key lifecycle distinction is documented in Puppeteer’s disconnect reference, close reference, and Browser API example.

Disconnect, close, or reconnect?

Operation Effect Use it when
await browser.disconnect() Detaches Puppeteer; the browser process and pages remain running. You want to release this Puppeteer connection while leaving the browser available.
await browser.close() Closes the browser and its associated pages. You are done with the browser and intend to shut it down.
await puppeteer.connect({ browserWSEndpoint }) Creates a Puppeteer connection to a browser exposed at that WebSocket endpoint. You need to control an already-running browser, including one previously disconnected from.

disconnect() returns a Promise<void>. Await it so your code does not move on before the detach operation completes. The documented behavior applies to browser instances launched by Puppeteer and instances attached through puppeteer.connect(). For a browser launched independently, connect using its WebSocket endpoint, do your work, then disconnect when you are finished with that client. See Puppeteer’s browser management guide.

Reconnect to a running browser

For a browser launched by the same script, get the endpoint before disconnecting:

const browserWSEndpoint = browser.wsEndpoint();
await browser.disconnect();

const browserAgain = await puppeteer.connect({ browserWSEndpoint });

For an independently managed browser, the process that launches or hosts it must provide a WebSocket endpoint that your application can reach. Pass that endpoint using the option documented by Puppeteer’s connect() API. Keep the endpoint available to the reconnecting process; do not assume it remains valid across a browser restart or every hosting setup. Puppeteer’s documentation demonstrates saving and reusing the endpoint, but does not promise endpoint longevity in third-party environments.

Keep ownership and cleanup clear

  1. Decide which component owns the browser process. The component that owns its lifecycle should decide when it is closed.
  2. Save the endpoint if another part of your application must reconnect.
  3. Disconnect a client when it should stop controlling the browser but the browser must remain alive.
  4. Close the browser through an attached Puppeteer connection when the browser itself should stop.

After disconnect(), treat that disconnected client as detached. Connect again to obtain a usable Puppeteer browser connection. If another supervisor or hosting service controls the process, its lifecycle rules also matter; Puppeteer’s API behavior alone does not establish how that external system handles a browser.

Other client examples

Disconnecting is a Puppeteer browser operation, so cURL and the Python and Node.js examples below are for ScreenshotNeo’s screenshot API, not for detaching a Puppeteer connection. They show the alternative when your task is to capture a website image or PDF without running and managing a browser yourself.

cURL

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

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
    timeout=90,
)
r.raise_for_status()
with open("shot.webp", "wb") as f:
    f.write(r.content)

Node.js

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const bytes = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(({ writeFile }) => writeFile('shot.webp', bytes));

See the ScreenshotNeo API documentation for request details. ScreenshotNeo is a website screenshot API and MCP server from Yorker Media; it returns an image or PDF from one GET request. These calls take a screenshot; they do not expose or manage a Puppeteer browser connection.

Or skip the browser setup

If you need a screenshot rather than a reusable Puppeteer browser session, ScreenshotNeo can capture a page with one request:

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

Cookie banners and consent overlays are accepted or removed before capture, along with newsletter popups and chat widgets; each cleanup 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. An MCP server lets AI agents, including Claude and Cursor, use screenshot tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots. Every feature is available on every plan. See the API docs, then sign up for 1,000 free screenshots a month with no card.

Configuration and behavior to consider

  • Browser lifecycle: Choose disconnect versus close based on whether the process should remain alive. Disconnecting does not mean the browser is terminated.
  • Endpoint availability: Store the WebSocket endpoint somewhere the reconnecting process can read it. A saved value only helps while the browser is still running and that endpoint is reachable.
  • External browser: If another tool or service launched the browser, use the endpoint it exposes and follow its process ownership rules.
  • Pages and state: Puppeteer documents that disconnect does not close pages. A subsequent connection can inspect the browser’s pages, as in the example. Do not infer that an external host will preserve a browser across its own restart or cleanup.
  • Shutdown: Use close() only when ending the browser session is intended. Ensure the code that calls it owns or is authorized to end that browser lifecycle.

Troubleshooting

Symptom Likely cause What to do
The browser disappears after cleanup. The code called browser.close(), or an external process manager stopped the browser. Use browser.disconnect() for a Puppeteer detach. Check the host or supervisor separately if the process still exits.
browser.pages() or another operation fails after disconnect. The Puppeteer client was detached and is no longer the active connection. Call puppeteer.connect() with a reachable WebSocket endpoint and use the returned browser object.
puppeteer.connect() cannot connect. The endpoint was not saved, is malformed, is unreachable, or the browser is no longer running. Capture browser.wsEndpoint() before disconnecting; verify the browser still exists and that the reconnecting process can reach its endpoint.
Reconnect succeeds but expected pages are missing. The browser or an external service may have closed those pages or replaced the browser process. Inspect await browser.pages() after reconnecting. Check lifecycle behavior in the process that owns the browser.
The script hangs during browser work. The page navigation or another awaited browser operation has not completed; this is separate from disconnect semantics. Use an appropriate navigation condition and add application-level timeouts or error handling around the work before disconnecting.
The browser remains running after the script exits. That is expected after disconnect(); detaching does not shut it down. Reconnect and call close() if you own the browser and intend to stop it, or let its external owner manage shutdown.

Performance, reliability, and cost

disconnect() is a connection lifecycle operation, not a browser shutdown operation. It leaves the process and pages running according to Puppeteer’s documented behavior, so the browser continues to use whatever resources its open pages and process require. If the application owns the browser and no one needs it, close it to end that browser session; if another component needs it, disconnect and leave lifecycle management to that owner.

Reconnection depends on a usable WebSocket endpoint and a browser that is still available. Save the endpoint before detaching, handle connection failures, and avoid relying on a third-party host retaining a process beyond the behavior it documents. Puppeteer’s API references do not state a monetary price for disconnecting; any infrastructure or hosted-browser costs are determined by the environment running the browser.

FAQ

Does disconnecting close my tabs?

No. Puppeteer’s browser management guide says disconnecting does not close the browser or its pages.

Can I disconnect from a browser I connected to?

Yes. The Browser API covers browser instances launched by Puppeteer and those attached with puppeteer.connect().

Can I use the same Browser object after disconnecting?

Use a new connection for subsequent browser control: reconnect with puppeteer.connect() and the saved endpoint.

Should I disconnect or close in a shared browser service?

Disconnect the client that is finished with its work. The component responsible for the browser’s lifecycle should decide whether and when to close the process.