ScreenshotNeo

BlogGuides

Puppeteer Browser Providers: How Browser Downloads Work

Learn which browser Puppeteer downloads, where it stores it, how to install a browser manually, and when a custom provider or puppeteer-core makes sense.

By the ScreenshotNeo team4 October 202611 min read

Puppeteer’s puppeteer package normally downloads a compatible Chrome for Testing build and chrome-headless-shell during installation, then stores them in $HOME/.cache/puppeteer. If your package manager blocks install scripts, the browser download may be skipped; run npx puppeteer browsers install to install it manually. For an intentionally managed local or remote browser, use puppeteer-core and supply the browser path or connection details yourself. Puppeteer installation guide

1. What Puppeteer downloads and where it puts it

Installing puppeteer downloads the Chrome for Testing build associated with that Puppeteer release. Starting with Puppeteer v21.6.0, installation also downloads chrome-headless-shell. The default cache directory is $HOME/.cache/puppeteer (on Windows, the home directory is resolved for the current account). The cache is separate from node_modules, so moving a project or changing the runtime account can make an existing browser appear missing.

The installation guide currently estimates downloads at about 170 MB on macOS, 282 MB on Linux, and 280 MB on Windows. Treat these as current approximate download sizes, not permanent guarantees; browser revisions and packaging change. See installation and troubleshooting.

2. Choose who manages the browser

Approach Who downloads and manages it? Use it when Main tradeoff
puppeteer default install Puppeteer’s install script You want the compatible default with the least setup. Install scripts must be permitted and the cache must persist into runtime.
Manual puppeteer browsers install You trigger Puppeteer’s browser installer Package-manager policy blocks postinstall scripts, or CI setup is explicit. You must include the browser installation step in setup and deployment.
@puppeteer/browsers API or CLI Your application or deployment pipeline You need to pin a build, platform, cache, or download base URL. You own the chosen version and deployment consistency.
Custom provider or mirror Your provider integration and source Your organization requires an internal artifact source. Custom providers are not officially supported; you own compatibility and maintenance.
puppeteer-core with a managed browser You or a remote browser service The browser is provisioned separately or is remote. You must configure the executable or remote connection and validate versions.

puppeteer is the batteries-included product and uses puppeteer-core internally. puppeteer-core does not download Chrome and does not use Puppeteer configuration files or environment variables for installation defaults. Puppeteer recommends it for remote browsers or when you manage browsers yourself. Package behavior and installation

3. Install the default browser

Fresh project

mkdir puppeteer-download-demo
cd puppeteer-download-demo
npm init -y
npm install puppeteer

Use this runnable script as shot.mjs. It launches Puppeteer’s downloaded browser, visits a URL, and writes a screenshot.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({headless: true});
try {
  const page = await browser.newPage();
  await page.goto('https://example.com', {waitUntil: 'networkidle2'});
  await page.screenshot({path: 'example.png', fullPage: true});
} finally {
  await browser.close();
}
node shot.mjs

Use a compatible Node.js release for the Puppeteer version you install. The current Puppeteer v25.12.0 documentation lists Node 22.12 or later; check the system requirements for your installed version and target platform.

Package manager skipped the install script

Some package manager configurations block dependency install scripts. In that case, the JavaScript package may be present while its browser is absent. Install the package as usual, then run Puppeteer’s browser installer in the project:

npx puppeteer browsers install

The installation guide also documents the equivalent Yarn, pnpm, and Bun invocation forms. For npm, its guide shows opting into Puppeteer’s install script through the package manager’s allowScripts configuration. Choose the policy that fits your project, then verify the browser install step actually ran. Automatic downloads and manual installation

4. Configure the cache and download behavior

Puppeteer configuration files are the recommended way to configure supported options. Place a .puppeteerrc.cjs in the project root, for example:

/** @type {import('puppeteer').Configuration} */
module.exports = {
  cacheDirectory: './.cache/puppeteer',
  // Set true only when this environment provisions its own browser.
  skipDownload: false,
  chrome: {
    skipDownload: false,
  },
};

With this relative cache, make sure both the install and runtime processes resolve the project root consistently, and preserve the cache in the deployed artifact or install it during deployment. After changing download-related settings, rerun the browser installer. A cache-directory change may require reinstalling Puppeteer or running the browser install command so the files are placed at the new path. Configuration guide

Setting Purpose Notes
cacheDirectory / PUPPETEER_CACHE_DIR Choose where browser builds are cached. Install and runtime need to agree on the path.
skipDownload / PUPPETEER_SKIP_DOWNLOAD Skip browser downloads during installation. Do not enable unless another step supplies the browser.
defaultBrowser / PUPPETEER_BROWSER Select Puppeteer’s default browser. Verify browser-specific download settings too.
executablePath / PUPPETEER_EXECUTABLE_PATH Point launch at a specific executable. The selected executable’s compatibility is your responsibility.
Chrome-specific skip setting Control Chrome download separately. Check the setting supported by your exact installed Puppeteer version.
Firefox-specific skip setting Control Firefox download separately. Firefox’s current documented default is skip download; configure it deliberately.

Environment-variable overrides take precedence where documented. Puppeteer’s stable configuration guide and API reference evolve; consult the reference matching your installed version rather than copying a setting from the next branch. Current configuration details are in the configuration guide and the configuration API.

5. Install a specific browser build with @puppeteer/browsers

The @puppeteer/browsers CLI supports an explicit Chrome build or release channel and can list installed browsers:

# Install the Chrome for Testing build corresponding to the stable channel
npx @puppeteer/browsers install chrome@stable

# Or pin an exact build ID
npx @puppeteer/browsers install chrome@154.0.8037.57

# Inspect cached browser installations
npx @puppeteer/browsers list

Use the browser and build supported by the Puppeteer version in your application. The v25.12.0 documentation maps Puppeteer to Chrome for Testing 154.0.8037.57 and Firefox 156.0.1. A floating channel such as stable is convenient but can resolve to a different build later; pin a build when repeatable deployments matter. The build IDs above describe that documented release, not a promise that they are suitable for every Puppeteer version. Browser management API and CLI · Install options

Programmatic install into a known cache

import {Browser, detectBrowserPlatform, install} from '@puppeteer/browsers';

const platform = detectBrowserPlatform();
if (!platform) throw new Error('Unsupported browser platform');

const installed = await install({
  browser: Browser.CHROME,
  buildId: '154.0.8037.57',
  platform,
  cacheDir: './.cache/puppeteer',
});
console.log('Browser executable:', installed.executablePath);

Install the API package as a direct dependency if you use this code in your project. A successful install returns the installed browser details, including its executable path. Persist the same cache directory into the runtime image or pass the returned path to your launch configuration.

Options that matter

  • browser, buildId, and platform select the binary. Set them deliberately in reproducible build pipelines.
  • cacheDir controls the install cache and should match the location your runtime can access.
  • baseUrl selects the download host. It is useful for mirrors; it is not itself a custom-provider implementation.
  • expectedHash can require a lowercase hexadecimal SHA-256 archive checksum. When provided, installation fails if the archive does not match. Obtain the trusted expected value through your own release or artifact process.
  • installDeps attempts to install system dependencies only for Chrome on Debian or Ubuntu and requires system privileges to run apt-get. It is not a general Linux dependency installer.
  • unpack defaults to true. With unpack: false, the API downloads the archive and returns its path instead of unpacking it.
  • downloadProgressCallback and logger support progress and logging behavior.

The supported platforms, archive utilities, and system library requirements differ. Chrome downloads use unzip on Linux/macOS and tar.exe on Windows; Firefox has its own unpacking requirements. Check system requirements before building a minimal container.

6. Use a custom provider or mirror

A custom provider implements where a browser archive comes from and where its executable lives inside the extracted archive. The @puppeteer/browsers API accepts provider implementations in order and retains its default provider as the final fallback. A mirror base URL may be simpler when archive layout and naming already match the default provider. Use a provider when the source or archive mapping differs.

Minimal integration shape, adapted from the official API example:

import {Browser, BrowserPlatform, install} from '@puppeteer/browsers';

class MirrorProvider {
  constructor(mirrorUrl) {
    this.mirrorUrl = mirrorUrl;
  }
  getName() {
    return 'internal-mirror';
  }
  supports(options) {
    return options.browser === Browser.CHROME;
  }
  getDownloadUrl(options) {
    const platform = options.platform;
    const file = platform === BrowserPlatform.LINUX
      ? 'chrome-linux64.zip'
      : platform === BrowserPlatform.WIN64
        ? 'chrome-win64.zip'
        : null;
    if (!file) return null;
    return new URL(`${this.mirrorUrl}/chrome/${options.buildId}/${file}`);
  }
  getExecutablePath(options) {
    if (options.platform === BrowserPlatform.LINUX) {
      return 'chrome-linux64/chrome';
    }
    if (options.platform === BrowserPlatform.WIN64) {
      return 'chrome-win64/chrome.exe';
    }
    throw new Error(`Unsupported platform: ${options.platform}`);
  }
}

const provider = new MirrorProvider('https://mirror.example.invalid');
await install({
  browser: Browser.CHROME,
  buildId: '154.0.8037.57',
  platform: BrowserPlatform.LINUX,
  cacheDir: './.cache/puppeteer',
  providers: [provider],
});

Important: replace the example host and archive layout with your real mirror’s verified layout. The example URL is deliberately non-routable. The provider must correctly resolve builds, supported platforms, archive URLs, and executable paths. Puppeteer states that custom providers are not officially supported and that users own compatibility, testing, and maintenance. Its compatibility guarantee is for Chrome for Testing binaries. BrowserProvider documentation

7. Use a separately managed or remote browser

For a local browser installed by your operating system or deployment image, use puppeteer-core and provide its executable path. Example:

import puppeteer from 'puppeteer-core';

const browser = await puppeteer.launch({
  executablePath: process.env.CHROME_PATH,
  headless: true,
});
try {
  const page = await browser.newPage();
  await page.goto('https://example.com', {waitUntil: 'domcontentloaded'});
  console.log(await page.title());
} finally {
  await browser.close();
}

Set CHROME_PATH to the actual executable installed in that environment. Puppeteer also accepts a channel when the browser is installed in a standard location. For a remote browser, use the connection method and endpoint provided by that browser host; do not assume a remote WebSocket endpoint is a local executablePath. Puppeteer warns that separately selected executables are used at the user’s risk, with its bundled browser as the compatibility baseline. Installation guide · Launch options

8. Troubleshooting browser downloads and launches

Symptom Likely cause Fix
Could not find Chrome (ver. ...) Postinstall scripts were blocked, download was skipped, or the cache is absent at runtime. Run npx puppeteer browsers install; check skip-download settings; verify install and runtime use the same cache and OS user.
Could not find expected browser locally Cache moved, home directory differs, or cache path changed between install and runtime. Set PUPPETEER_CACHE_DIR consistently or configure cacheDirectory, then reinstall the browser into that path.
Browser exists in build logs but not in deployed container The cache is outside the copied artifact or not present in the final image. Install during the final image build, copy the cache intentionally, or use a project-local cache that is included in the image.
Browser launches locally but exits in Linux CI/container Required OS libraries or archive utilities are missing, or the runtime user cannot execute/read the binary. Check the current system requirements; install required libraries and unpacking tools; verify file permissions and the runtime user.
Custom mirror download returns 404 or cannot launch Provider URL, platform archive name, executable path, or build is wrong. Validate the archive and executable layout for each platform and pin a build known to match your Puppeteer version.
Firefox is missing although Chrome installed Firefox downloads are separately configured and the documented default may skip Firefox. Enable Firefox downloading in the config supported by your version and explicitly install the browser.
Download stalls or fails behind a corporate proxy Proxy environment or proxy support is not configured for the downloader. Set HTTP_PROXY, HTTPS_PROXY, and NO_PROXY as appropriate; for @puppeteer/browsers, install the documented proxy-agent peer dependency.
Checksum verification fails The downloaded archive differs from the expected SHA-256 value or the expected value belongs to a different build. Confirm the build, platform, and trusted checksum source; do not disable verification without understanding the discrepancy.

For download and cache diagnostics, run the browser CLI with NODE_DEBUG enabled:

NODE_DEBUG="puppeteer:browsers:*" npx @puppeteer/browsers install chrome@stable

The documented debug channels include cache operations, file utilities, installation progress, and launcher state. On supported installations, proxy variables are honored when the documented proxy-agent package is available. See Puppeteer troubleshooting and @puppeteer/browsers documentation.

9. Performance, reliability, and cost considerations

  • Build time and bandwidth: browser downloads are large. Download once into a persistent build cache where your CI supports it, or provision the browser in a controlled image build. Avoid downloading a fresh copy on every application start.
  • Repeatability: pin the Puppeteer package and browser build together for stable deployments. A moving channel is easier to maintain but can change over time. Keep install and runtime browser versions aligned.
  • Cache correctness: the cache belongs to a particular filesystem, platform, and user context. Make its path and lifecycle explicit in containers and serverless deployments.
  • Compatibility: Chrome for Testing is Puppeteer’s tested baseline. Custom builds, system Chrome, and custom providers transfer more validation work to your team.
  • Operational cost: budget for download bandwidth, image or volume storage, and the CPU and memory used by browser processes. The Puppeteer documentation’s download-size estimates help with transfer planning but are not resource-use benchmarks.
  • Integrity: for controlled installation pipelines, pin a build and consider expectedHash for archive verification. Protect the trusted checksum and mirror configuration as part of your artifact supply chain.

10. Or skip the browser setup

If your task is to capture a website screenshot rather than automate a browser session, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns an image or PDF; see the API documentation for its options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
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()
open("shot.webp", "wb").write(r.content)
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}`);
await import('node:fs/promises').then(({writeFile}) =>
  writeFile('shot.webp', Buffer.from(await res.arrayBuffer()))
);
  • Cookie banners are accepted and more than 60 known consent platforms, newsletter popups, and chat widgets are removed before capture; each step can be turned off.
  • Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing; response headers identify the page verdict and billing status.
  • An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents and MCP clients.
  • The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

Start with 1,000 free screenshots a month, no card required.

11. Frequently asked questions

Does Puppeteer download Google Chrome?

The standard puppeteer package downloads Chrome for Testing, the browser build associated with Puppeteer’s release. It also downloads chrome-headless-shell in current releases.

Can I change the download server without implementing a provider?

For supported browser archive layouts, baseUrl or the Chrome download base URL may be sufficient. If URL mapping or archive structure differs, you need a provider implementation and must validate it.

Can Puppeteer use Firefox?

Yes. Firefox is supported, but its download controls are separate and the current documented default skips its download. Check your version’s Firefox configuration before expecting it in the cache.

Should I commit the browser cache to source control?

Usually configure a deployment or CI cache instead. Browser archives are large and platform-specific; whichever approach you choose, ensure the runtime receives the correct build and cache path.

Can I use an arbitrary Chromium binary?

You can point Puppeteer at an executable, but compatibility is your responsibility. Puppeteer tests and guarantees Chrome for Testing binaries.