ScreenshotNeo

BlogGuides

Puppeteer DownloadPolicy: Download Options Explained

Choose between Puppeteer’s four download policies, configure a save path, and handle GUID-named files. Includes runnable examples and common fixes.

By the ScreenshotNeo team4 October 20266 min read

Puppeteer’s DownloadPolicy has four values: deny, allow, allowAndName, and default. Use deny to block page downloads; allow to save them in a chosen directory; allowAndName to save them under download GUIDs; and default to defer to Chrome’s default behavior when available, otherwise denying downloads. Set downloadPath for either allow policy.

1. The four Puppeteer download policies

Policy Behavior Use it when Path required?
deny Denies all download requests. The test or automation must not download files. No
allow Allows downloads and uses the configured download path. Chrome handles the suggested filenames. You need files saved in a known directory and want normal filename behavior. Yes
allowAndName Allows downloads and names each file with its download GUID. Your automation can track GUIDs and does not depend on the website’s suggested filename. Yes
default Uses Chrome’s default behavior if available; otherwise denies downloads. You explicitly want browser-default behavior. No

The important naming distinction is that allowAndName does not preserve the source website’s suggested name. It uses a GUID. Choose allow when a useful human-readable filename matters.

2. Configure download behavior in Puppeteer

For a browser launched by Puppeteer, provide downloadBehavior in the launch options. Set an absolute, writable directory for either allow policy.

import puppeteer from 'puppeteer';
import path from 'node:path';

const downloadPath = path.resolve('./downloads');
const browser = await puppeteer.launch({
  headless: true,
  downloadBehavior: {
    policy: 'allow',
    downloadPath,
  },
});

try {
  const page = await browser.newPage();
  await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
  await page.click('a[href$=".pdf"]');
  // Wait for the download to finish before inspecting the directory.
  await new Promise(resolve => setTimeout(resolve, 1500));
} finally {
  await browser.close();
}

The example assumes the download directory already exists and the process can write to it. For deterministic automation, create the directory before launching and wait for download completion using an application-appropriate signal rather than relying on a fixed delay. Consult the Puppeteer LaunchOptions API and the DownloadBehavior API for the version you use.

Block downloads explicitly

const browser = await puppeteer.launch({
  downloadBehavior: { policy: 'deny' },
});

This is useful for tests that should verify links without allowing files to be written. A page may still initiate a navigation or show its own error; denying a download is not the same as removing the download link from the page.

Use GUID-based names

const browser = await puppeteer.launch({
  downloadBehavior: {
    policy: 'allowAndName',
    downloadPath: '/absolute/path/to/downloads',
  },
});

With this policy, the file’s name is the download GUID. If downstream code needs a particular name, maintain a mapping from download events or protocol events to the GUID and rename the completed file afterward. Do not assume the site’s Content-Disposition filename will be retained.

3. Which policy should you choose?

  1. Choose deny when downloads are unwanted or could pollute test output.
  2. Choose allow with a dedicated directory for ordinary downloads that your code will inspect or archive.
  3. Choose allowAndName only when GUID-based filenames are acceptable or your workflow records a mapping.
  4. Choose default when you specifically want Chrome’s default behavior. For predictable automated results, select an explicit policy instead.

4. Browser context and Chrome DevTools Protocol

At the protocol level, Browser.setDownloadBehavior accepts a policy, an optional download path, an optional browser context ID, and an eventsEnabled flag. If the context ID is omitted, the behavior applies to the default browser context. The protocol reference gives eventsEnabled a default of false. The method is marked experimental, so check the protocol version matching your Chromium build.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
const cdp = await browser.target().createCDPSession();
await cdp.send('Browser.setDownloadBehavior', {
  behavior: 'allow',
  downloadPath: '/absolute/path/to/downloads',
  eventsEnabled: true,
});

// Subscribe before triggering the page download.
cdp.on('Browser.downloadWillBegin', event => {
  console.log('Download started:', event.guid, event.suggestedFilename);
});
cdp.on('Browser.downloadProgress', event => {
  console.log('Download progress:', event.guid, event.state);
});

This lower-level example uses Chrome DevTools Protocol (CDP) directly. Use the API exposed by your installed Puppeteer and Chromium versions: protocol names and availability can vary. In particular, do not pass Puppeteer’s DownloadBehavior object shape blindly to CDP; Puppeteer uses policy, while the protocol method calls the field behavior. See the CDP Browser.setDownloadBehavior reference.

5. Runtime downloads versus installing Puppeteer’s browser

DownloadPolicy controls files downloaded by a page while a browser is running. It does not control whether Puppeteer downloads a browser binary during package installation. The Puppeteer installation guide explains that the puppeteer package normally downloads a compatible browser, while puppeteer-core does not download Chrome and is intended for users who manage a browser or connect to a remote one. If install scripts are blocked, the guide documents npx puppeteer browsers install as a manual installation command.

6. Troubleshooting

Symptom Likely cause Fix
Allowed download fails or no file appears. The path is missing, relative where an absolute path is expected, or not writable. Create the directory, use an absolute path, and check the browser process’s permissions.
File has an unexpected name. The policy is allowAndName, which names files by GUID. Use allow for normal browser naming, or map the GUID and rename after completion.
Policy setting appears ignored. The setting may have been applied to a different browser context, or the connected browser/protocol version may not support the expected behavior. Apply the setting to the correct context; check the Puppeteer and Chromium versions and the protocol reference.
Download events are not received. CDP event emission is disabled unless eventsEnabled is enabled in the protocol method. Set eventsEnabled: true and register listeners before triggering the download.
Installation browser is missing. This is a browser installation issue, separate from runtime DownloadPolicy. Follow the installation guide; for puppeteer-core, provide or connect to a managed browser.
Test checks the directory too soon. The download is asynchronous and has not finished writing. Wait for a completion event or a reliable application-level completion condition before reading the file.

7. Performance, reliability, and cost

The policy itself does not make a download faster; it determines whether Chrome permits the file and how it names or locates it. Runtime and disk costs come from the downloaded files and the browser process. Use a dedicated temporary directory per run or worker to prevent collisions, clean it after successful processing, and allow enough disk space for large files. For reliable automation, explicitly configure behavior, use a writable absolute path, and detect completion rather than assuming a click means the file has finished saving. No product-specific performance or cost figures are implied by these settings.

8. Or skip the browser setup

If your task is to capture a webpage rather than automate its download flow, ScreenshotNeo returns an image or PDF from one API request. 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}`);

ScreenshotNeo removes cookie banners, popups, and chat widgets before the shot. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots, and the free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card.

9. FAQ

Does default always allow downloads?

No. It delegates to Chrome’s default behavior when available; the protocol documentation says otherwise downloads are denied.

Can I keep the website’s filename with allowAndName?

No. That policy uses download GUIDs as filenames. Use allow if normal browser filename behavior is needed.

Does DownloadPolicy choose whether Puppeteer installs Chrome?

No. It applies to page downloads in a running browser. Browser installation is configured separately.