How to Close a Puppeteer Browser
Use `await browser.close()` to shut down Puppeteer and its pages. Learn when to disconnect, close one page or context, and handle cleanup safely.
To shut down a Puppeteer browser and all pages associated with it, call await browser.close(). Awaiting the promise lets the asynchronous cleanup finish before your script continues or exits.
await browser.close();
Use browser.disconnect() instead when Puppeteer should stop controlling a browser but leave the browser process and its pages running. To close only one page, use page.close(); to close an isolated browser context and its pages, use context.close().
1. Close a launched browser reliably
Put browser shutdown in a finally block so it runs whether your page work succeeds or throws an error. This is especially useful in scripts, workers, and scheduled jobs that should not leave launched browser processes behind.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com');
console.log(await page.title());
} finally {
await browser.close();
}
This example uses ECMAScript modules and assumes Puppeteer is installed in the project. The key cleanup operation is the awaited browser.close().
2. Choose the cleanup scope
| What should stop? | Call | Effect |
|---|---|---|
| One page | await page.close() |
Closes that page. The browser and its other pages remain open. |
| An isolated context | await context.close() |
Closes that context and its pages. The default browser context cannot be closed this way. |
| The browser | await browser.close() |
Closes the browser and all pages associated with it. |
| Puppeteer’s connection only | await browser.disconnect() |
Detaches Puppeteer while leaving the browser process and its pages running. |
Use the narrowest operation that matches who owns the resource. If your code launched the browser and owns its lifetime, close it. If another process owns a shared browser and your code is only a client, disconnect when your work is done.
3. Close a page or isolated context
Close one page
const page = await browser.newPage();
try {
await page.goto('https://example.com');
// Read or capture page content.
} finally {
await page.close();
}
page.close() returns a promise and accepts an optional { runBeforeUnload?: boolean } option. For example:
await page.close({ runBeforeUnload: true });
Use that option when you need the page’s before-unload behavior to run. It is not a substitute for closing the browser when the whole session should end.
Close an isolated context
const context = await browser.createBrowserContext();
try {
const page = await context.newPage();
await page.goto('https://example.com');
// Work in this isolated context.
} finally {
await context.close();
}
Closing the context closes its pages. Do not call context.close() on the default browser context; that context cannot be closed using this method.
4. Decide whether to close or disconnect
browser.close() shuts down the browser. browser.disconnect() only ends Puppeteer’s control connection: the browser and open pages remain available. Puppeteer’s management guide states, “Unlike browser.close(), browser.disconnect() does not shut down the browser or close any pages.”
This distinction matters when you connect to a browser managed by a separate service:
import puppeteer from 'puppeteer';
const browser = await puppeteer.connect({ browserWSEndpoint: process.env.BROWSER_WS_ENDPOINT });
try {
const page = await browser.newPage();
await page.goto('https://example.com');
} finally {
// Leave the externally managed browser running.
await browser.disconnect();
}
If you intend to shut down the browser itself, use browser.close() instead. For a connected browser, browser.process() returns null; do not rely on it for a local child-process handle.
5. Handle cancellation and process signals
Puppeteer launch options accept an AbortSignal; aborting that signal closes the browser. Launch options also include default handlers for SIGHUP, SIGINT, and SIGTERM that describe browser close or kill behavior. These process-level settings address shutdown triggers; application cleanup can still use try/finally.
import puppeteer from 'puppeteer';
const controller = new AbortController();
const browser = await puppeteer.launch({ signal: controller.signal });
try {
const page = await browser.newPage();
await page.goto('https://example.com');
// Your work may decide to cancel the launched browser:
// controller.abort();
} finally {
await browser.close();
}
Consult the launch options for the version you install before changing signal-handler settings; their exact defaults are version-specific.
6. Troubleshoot browser shutdown
| Symptom | Likely cause | Fix |
|---|---|---|
| Chrome is still running after cleanup | The code called browser.disconnect(), which leaves the browser process running, or skipped shutdown on an error path. |
Use await browser.close() when your code owns the browser. Put cleanup in finally. |
| Other pages stopped unexpectedly | The code closed the browser rather than only the page or context it owned. | Use page.close() for one page or context.close() for an isolated context. |
browser.process() returns null |
The browser was obtained through puppeteer.connect(); there may be no local child process handle. |
Use the connection’s documented lifecycle operation. Disconnect to release Puppeteer’s connection; close only if you intend to shut down the browser. |
| Cleanup does not run after an exception | The close call is only on the success path. | Move the awaited close operation into a finally block. |
| Trying to close the default context fails | The default browser context cannot be closed with context.close(). |
Close a separately created context, or close the browser if the entire session should end. |
7. Performance, reliability, and cost
Closing at the right scope releases the browser resources your code owns and makes repeated automation runs easier to manage. Await the close operation so the shutdown is part of the control flow. If the browser is shared or externally managed, disconnecting avoids shutting down a resource your process does not own.
This Puppeteer lifecycle operation has no separate API request cost. Your own infrastructure still determines the cost of running a browser process and the pages it hosts; the research sources provide no benchmark or fixed resource estimate. For reliable cleanup, decide who owns the browser, use page or context cleanup for partial work, and ensure the owning code path closes the browser after use.
8. Or skip the browser setup
If your goal is to get a webpage screenshot rather than manage a Puppeteer browser, ScreenshotNeo provides a screenshot API and MCP server. Its one-call API returns an image or PDF, and its docs list the available capture options.
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}`);
await Bun.write('shot.webp', res);
See the ScreenshotNeo API documentation for authentication and options. Cookie banners, popups, and chat widgets are removed before capture. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Learn about ScreenshotNeo, then sign up for 1,000 free screenshots a month with no card.
9. FAQ
Does browser.disconnect() close Chrome?
No. It detaches Puppeteer and leaves the browser process and its pages running.
How do I close Chrome after Puppeteer finishes?
If your code launched and owns that browser, await browser.close() after the work is complete.
Can I close just one Puppeteer tab?
Yes. Call await page.close() for that page; other pages in the browser can remain open.
Can I close the default browser context?
No. The default context cannot be closed with context.close(). Close a separately created context or shut down the browser, depending on the intended scope.


