Puppeteer DownloadBehavior: Configure Browser Downloads
Configure Puppeteer’s DownloadBehavior to allow, block, or control page-triggered downloads. Learn the policies, downloadPath setup, and common fixes.
DownloadBehavior controls how a Puppeteer browser handles files that a web page asks it to download. To save page downloads in a chosen directory, pass downloadBehavior with policy: 'allow' and an absolute downloadPath to puppeteer.launch():
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({
downloadBehavior: {
policy: 'allow',
downloadPath: '/absolute/path/to/downloads',
},
});
The setting is separate from installing Puppeteer’s browser binary. This guide covers the current documented API references for Puppeteer 25.12.0. Confirm support in your installed version and browser/protocol combination if your setup depends on a particular context or connection path.
1. Choose a download policy
DownloadBehavior has a policy and an optional downloadPath. Choose the policy that matches what your automation should do:
| Policy | Effect | Path requirement |
|---|---|---|
deny |
Block downloads. | No path requirement is documented. |
allow |
Permit downloads. | downloadPath is required. |
allowAndName |
Permit downloads and name files according to their download GUIDs. | downloadPath is required. |
default |
Use the browser’s default behavior. | No path requirement is documented. |
Use allow when your workflow expects recognizable filenames in a known directory. Use allowAndName when GUID-based naming is appropriate; do not expect the original filename under that policy. The default policy leaves handling to the browser and may not provide the predictable destination your automation needs.
2. Configure downloads when launching Puppeteer
Here is a complete runnable example that opens a page, clicks a download link, and closes the browser. Replace the URL and selector with values from the page you automate. The destination directory must be suitable for the host and writable by the process; the API reference does not promise to create it automatically.
import puppeteer from 'puppeteer';
const downloadPath = '/absolute/path/to/downloads';
const browser = await puppeteer.launch({
downloadBehavior: {
policy: 'allow',
downloadPath,
},
});
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.locator('a.download-link').click();
// Add an application-appropriate completion check before consuming the file.
} finally {
await browser.close();
}
This example configures download handling; it intentionally does not assume a particular download event or file-completion signal for every browser and protocol combination. Your completion check should match the browser version and workflow, and should account for a file still being written before another step reads it.
Use GUID-based filenames
Change the policy and keep the required destination path:
const browser = await puppeteer.launch({
downloadBehavior: {
policy: 'allowAndName',
downloadPath: '/absolute/path/to/downloads',
},
});
Files are named according to their download GUIDs, so downstream code should not rely on the original suggested filename.
Block downloads or retain browser defaults
const blockDownloads = await puppeteer.launch({
downloadBehavior: { policy: 'deny' },
});
const browserDefaults = await puppeteer.launch({
downloadBehavior: { policy: 'default' },
});
These policies are useful when downloads should be prevented or when the browser’s own default handling is desired. They do not configure a custom destination.
3. Configure behavior when connecting to a browser
ConnectOptions.downloadBehavior sets the download behavior for the context. The common options interface is used for connecting, and LaunchOptions extends it, which is why the same option can be supplied at launch.
import puppeteer from 'puppeteer';
const browser = await puppeteer.connect({
browserWSEndpoint: process.env.PUPPETEER_WS_ENDPOINT,
downloadBehavior: {
policy: 'allow',
downloadPath: '/absolute/path/to/downloads',
},
});
try {
const page = await browser.newPage();
await page.goto('https://example.com');
// Trigger the page download here.
} finally {
await browser.disconnect();
}
Use the WebSocket endpoint for the browser you are connecting to. Download behavior depends on the installed Puppeteer version and the browser/protocol in use; verify the exact combination when connection-level behavior matters.
4. Set a usable downloadPath
For allow and allowAndName, supply downloadPath. Prefer an absolute path so the target is unambiguous across working directories. Before running the automation, ensure that:
- The directory exists, unless your own setup creates it.
- The user running Puppeteer can write to the directory.
- The path is valid inside the environment where the browser process runs, such as a container or remote host.
- Your job keeps downloaded files isolated if multiple browser jobs could write there at once.
Puppeteer’s API reference specifies that the path is required for the two allow policies; it does not establish directory creation behavior. Create and validate the directory in your application or deployment setup rather than relying on undocumented behavior.
5. Distinguish page downloads from browser installation
There are two different operations that are easy to confuse:
- Page-triggered file download at runtime: use
downloadBehaviorto set browser handling and, for allow policies, a destination path. - Installing the browser binary: the
puppeteerpackage downloads a compatible browser during installation.puppeteer-coredoes not download Chrome when installed.
If an install environment blocks package scripts and the Puppeteer-managed browser is missing, the documented manual remedy is:
npx puppeteer browsers install
That command installs a browser. It does not set the directory for files downloaded by a page.
6. Troubleshoot common problems
| Symptom | Likely cause | What to do |
|---|---|---|
| The API rejects or ignores the behavior configuration. | The installed version or browser/protocol path may not support the behavior as expected. | Check the API reference for your installed Puppeteer version and confirm the exact browser and connection path. |
| No file appears at the expected location. | The page may not have triggered a download, the policy may be deny or default, or the path may be on a different host. |
Confirm the click or download request occurred, set an allow policy with an absolute path, and check the filesystem where the browser runs. |
| A path-related error occurs with an allow policy. | downloadPath is absent or unusable. |
Provide the required path, create the directory in your setup, and grant the browser process write access. |
| The saved file has an unexpected name. | allowAndName names downloads using their GUIDs. |
Use allow if you need browser-suggested filenames, or adapt downstream processing to GUID names. |
| The next step reads a partial or missing file. | The download may not have finished when the job continued. | Add a completion check appropriate to your browser/protocol and only consume the file after it is complete. |
| Puppeteer launches without a managed browser. | The browser binary was not downloaded during installation, often because install scripts were blocked, or the package is puppeteer-core. |
Install a compatible browser with npx puppeteer browsers install, or configure your environment to use an available browser. This is distinct from downloadBehavior. |
7. Performance, reliability, and cost
DownloadBehavior itself selects browser handling; it does not make the remote server generate a file faster or guarantee that a download will succeed. Reliability depends on the page’s download trigger, network, filesystem permissions, available disk space, and the browser/protocol combination.
- Keep download directories bounded and clean them according to your application’s retention needs.
- Use separate paths or unique job directories when concurrent tasks might produce files with overlapping names.
- Wait for a reliable completion condition before parsing, uploading, or moving a downloaded file.
- Page downloads use your browser and application infrastructure. Account for browser runtime, network transfer, storage, and any destination service costs; Puppeteer’s API reference gives no pricing or performance benchmark for this option.
8. Or skip the browser setup
If the task is to capture a webpage rather than download a file from it, ScreenshotNeo provides a one-request screenshot API and an MCP server for AI agents. It is a different workflow from Puppeteer page downloads. See the ScreenshotNeo API documentation for request 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);
ScreenshotNeo removes cookie banners, newsletter 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. 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 required.
9. Frequently asked questions
Does DownloadBehavior change where Chrome is installed?
No. It controls handling of page-triggered file downloads. Browser installation is a separate Puppeteer setup step.
Can I use allow without a downloadPath?
The documented API requires downloadPath for both allow and allowAndName.
Will allowAndName preserve the filename from the website?
No. That policy names files according to their download GUIDs.
Does Puppeteer create the destination directory?
The reviewed API reference does not specify that behavior. Ensure the directory exists and is writable in your environment.
Which browser and protocol combinations support this?
The references do not establish a complete compatibility matrix. Check the stable API reference for your installed version and validate the browser/protocol path your application uses.


