ScreenshotNeo

BlogGuides

Puppeteer Headless Mode: How It Works and When to Use It

Learn what Puppeteer’s headless modes do, when to choose Chrome or chrome-headless-shell, and how to capture a page with runnable code.

By the ScreenshotNeo team4 October 20269 min read

Puppeteer runs Chrome in headless mode by default. In the current API, headless: true selects Chrome’s new headless mode, which shares the regular Chrome browser code path. Set headless: 'shell' to use the separate chrome-headless-shell binary, known as the old headless mode. Use headless: false when you need a visible browser for debugging.

The practical choice is straightforward: start with the default when browser compatibility matters; consider shell for automation that does not need the complete Chrome feature set; switch to headful mode when you need to see what is happening. Puppeteer’s documentation describes shell as more performant for some automation, but supplies no speed multiplier or benchmark, so measure your own workload before making that tradeoff. Puppeteer headless modes guide

1. What Puppeteer headless mode means

Headless means that browser automation runs without a visible browser window. It still uses a browser engine to load and render pages; it does not mean that there is no browser. Puppeteer controls Chrome or Firefox through browser automation protocols and supports tasks such as UI testing, form submission, screenshots, PDF generation, tracing, and crawling single-page applications. Puppeteer documentation

Headless mode is useful for repeatable automation in scripts, build pipelines, and server processes where opening a desktop window is unnecessary. It can also make screenshots and PDFs without requiring a person to operate the browser. Headful mode remains useful when you need to inspect rendering, follow a flow visually, or debug a problem that is hard to understand from logs.

2. The three launch choices

Setting What it launches When to choose it
headless: true Chrome’s new headless mode Default choice when you want behavior aligned with regular Chrome.
headless: 'shell' The separate chrome-headless-shell binary, the old headless mode Consider it when performance matters and your task does not need Chrome’s complete feature set. Verify compatibility with your pages.
headless: false Headful Chrome with a visible browser window Use it to observe browser behavior while developing or debugging.

The API default is true. Setting devtools: true forces headful mode, so a configuration that enables DevTools is not a headless run even if you expected the browser to stay hidden. LaunchOptions API reference

3. Install Puppeteer and capture a screenshot

The example below installs Puppeteer, launches the default new headless mode, opens a page, waits for navigation, saves a full-page PNG, and closes the browser even if an operation fails. It uses JavaScript, Puppeteer’s language.

npm init -y
npm install puppeteer

Save this as screenshot.mjs, then run node screenshot.mjs:

import puppeteer from 'puppeteer';

const url = 'https://example.com';
const browser = await puppeteer.launch({ headless: true });

try {
  const page = await browser.newPage();
  await page.setViewport({ width: 1440, height: 900 });
  await page.goto(url, { waitUntil: 'networkidle2', timeout: 45_000 });
  await page.screenshot({ path: 'page.png', fullPage: true });
  console.log('Saved page.png');
} finally {
  await browser.close();
}

networkidle2 waits for network activity to settle under Puppeteer’s navigation conditions. It is not a guarantee that every page’s application-specific content is ready: analytics, streaming requests, and long-lived connections can keep activity going, while a site may render important content after the network quiets. If a target has a known ready element, wait for that selector instead:

await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 45_000 });
await page.waitForSelector('main article', { timeout: 15_000 });
await page.screenshot({ path: 'article.png', fullPage: true });

For a one-off visible debugging run, change the launch options:

const browser = await puppeteer.launch({
  headless: false,
  slowMo: 250,
});

slowMo slows Puppeteer operations to make them easier to observe. Remove it from normal automation; it adds time and is intended to help debugging. Puppeteer debugging guide

4. New headless or headless shell?

Choose the default new mode for browser fidelity

The new headless mode is part of regular Chrome. Puppeteer’s supported-browser documentation says Chrome for Testing supports headless and headful operation using the same code path. This makes it the sensible baseline for testing behavior expected to match a normal Chrome session. Supported browsers

Try shell only when the tradeoff fits

The old headless mode is now a separate program, chrome-headless-shell. Puppeteer describes it as not completely matching regular Chrome behavior, while noting it is currently more performant for automation that does not need the complete feature set. That statement is qualitative; the documentation does not report numerical comparisons.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({ headless: 'shell' });
try {
  const page = await browser.newPage();
  await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
  await page.screenshot({ path: 'shell.png' });
} finally {
  await browser.close();
}

Before switching a production workload to shell, compare output and behavior on representative pages. Include features your job relies on, such as rendering, downloads, media, graphics, or browser APIs. Keep the same Puppeteer version, browser pairing, machine, viewport, network conditions, and test URLs in both runs. Record compatibility and end-to-end job duration; do not assume a speed gain based on a different workload or environment.

5. Launch options and browser setup

The headless setting is one part of puppeteer.launch(). These options are especially relevant when deciding how and where a browser runs:

Option Use
headless true for new headless, 'shell' for the old-mode binary, or false for a visible browser. Defaults to true.
devtools Opens DevTools and forces headful mode when true.
slowMo Slows automation operations to make them observable during debugging.
executablePath Selects a specific browser executable. Puppeteer warns that it is only guaranteed to work with its bundled browser.
channel Uses a regular Chrome installation from a known system location, when available.
args Passes extra command-line arguments. Prefer defaults unless you have a specific reason to change them.
timeout Sets the maximum time in milliseconds to wait for the browser to start; launch option default is 30 seconds.
dumpio Forwards browser process output to Node’s standard output and error streams, which can help diagnose launch problems.

For most projects, install puppeteer. Its installation downloads a compatible Chrome for Testing and a chrome-headless-shell binary. Use puppeteer-core when connecting to a remote browser or managing browser installation yourself; it does not download Chrome. With a managed browser, supply an appropriate executablePath or channel. See the installation guide and configuration reference.

Installation scripts may be blocked by package-manager policy. If the browser download was skipped, install the required browser explicitly:

npx puppeteer browsers install

Check the documentation for the Puppeteer version pinned in your project before relying on a version-sensitive launch option or browser pairing. Browser versions and behavior change over time.

6. Migration: why older scripts may behave differently

Before Puppeteer v22, the old headless mode was the default. The Puppeteer changelog marks v22.0.0, dated February 5, 2024, as the release that enabled the new headless mode by default. The changelog also records that Puppeteer began downloading chrome-headless-shell by default for old-headless mode in v21.10.0. Puppeteer changelog

When upgrading, check whether your code relied on the implicit default or expected old-headless behavior. If a workflow specifically needs shell, state that in the launch options with headless: 'shell'; if it expects regular Chrome behavior, use headless: true. Rerun representative screenshots and browser checks after changing the Puppeteer or browser version.

7. Performance, reliability, and cost

Performance

Shell may be faster for tasks that do not need the full Chrome feature set, according to Puppeteer’s qualitative guidance. There is no official speed multiplier in the cited documentation. Benchmark the actual end-to-end task, including browser startup, page navigation, readiness waits, and output generation. Reuse a browser process for multiple pages when it fits the workload, and close pages and browsers when finished to avoid leaking processes.

Reliability

Choose an explicit headless setting when mode selection matters, pin Puppeteer in the project, and deploy the browser version that matches that package. Wait for page-specific readiness when network-idle conditions do not describe the page. Always close the browser in a finally block, as in the examples. For container deployment, account for Chrome’s system dependencies and sandbox requirements; consult Puppeteer’s troubleshooting guide and Docker guide.

Do not disable Chrome’s sandbox casually. Puppeteer’s troubleshooting documentation describes the sandbox as a protection against untrusted web content. If your environment cannot support it, understand the security implications and follow the deployment guidance for your environment.

Cost

Running Puppeteer yourself means you manage the compute, browser installation, runtime dependencies, storage for outputs, and engineering time needed to operate and debug it. The cited Puppeteer material does not provide a universal operating cost: it depends on the workload and hosting environment. Measure resource use and job duration on your own infrastructure rather than assuming headless mode makes browser work free.

8. Troubleshooting common problems

Symptom Likely cause What to do
Could not find Chrome (ver. ...) The package manager blocked Puppeteer’s install script, or the expected browser was not installed. Run npx puppeteer browsers install, or configure the package manager to allow the Puppeteer install script. Confirm the installed browser matches the package.
Browser fails to launch with a missing shared library The Linux environment lacks a required Chrome system dependency. Install the dependencies required for Chrome on that distribution and consult Puppeteer’s system requirements and troubleshooting pages.
No usable sandbox! The host does not provide a usable Chrome sandbox configuration. Configure the host/container to support the sandbox. Follow the official environment-specific guidance; do not treat disabling the sandbox as a routine fix.
Shell output differs from regular Chrome chrome-headless-shell does not completely match regular Chrome behavior. Try headless: true and compare on the same workflow. Use the mode whose behavior meets the requirement.
Navigation hangs or times out The chosen navigation condition may never become true, or the page has slow or persistent network activity. Try domcontentloaded and wait for a meaningful selector with page.waitForSelector(). Set a timeout that fits the task and handle timeout failures.
Screenshot is blank or misses content The page may not have rendered the target content when the capture ran, or the wrong page state was reached. Wait for a page-specific element, verify the final URL and viewport, and reproduce visibly with headless: false.
Browser appears invisible while debugging The launch remains headless, or DevTools/headful settings are not configured as expected. Use headless: false; optionally add slowMo. Note that devtools: true forces headful mode.
Browser processes accumulate after failures Cleanup did not run on an exception or early return. Wrap browser work in try/finally and call browser.close() in the finalizer.

For deeper diagnostics, Puppeteer documents dumpio: true to forward browser logs and ways to inspect protocol activity. Logs can contain sensitive information, so review them before sharing. Debugging guide

9. Or skip the browser setup

If your goal is a website screenshot rather than browser automation, ScreenshotNeo provides a screenshot API and MCP server. Its one-call API returns an image or PDF, and its options include full-page capture, viewport and device presets, selector capture, waiting, custom CSS and JavaScript, and more. 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}`);
  • Cookie banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
  • Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing; response headers report the page verdict and billing status.
  • An MCP server gives AI agents tools to take screenshots, inspect page information, and capture PDFs.
  • 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000 screenshots.

Sign up free for ScreenshotNeo and get 1,000 screenshots a month with no card.

10. Frequently asked questions

Is Puppeteer headless by default?

Yes. The current launch option defaults to headless: true, which selects Chrome’s new headless mode.

Is headless: 'shell' the same as headless: true?

No. Shell launches a separate binary called chrome-headless-shell. It does not completely match regular Chrome behavior.

Does headless mode make Puppeteer faster?

Headless mode avoids displaying a browser window, but the documentation does not establish a universal speed advantage. Puppeteer describes shell as more performant for some automation, without giving a numerical benchmark.

Can I use Puppeteer without downloading Chrome?

Yes. puppeteer-core does not download a browser. It is intended for remote connections or environments where you manage the browser, and requires the appropriate connection or browser launch configuration.

When should I use headful mode?

Use headless: false when you need to see the browser while diagnosing page behavior. For slower, more observable steps, add slowMo.