Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Set Puppeteer’s browser download behavior before the page starts a download, and provide an absolute, writable downloadPath. The current option shape is downloadBehavior: { policy: 'allow', downloadPath: '/absolute/path/to/downloads' }. Use the same setting on the browser context that owns the page; changing Puppeteer’s browser cache directory does not change where page downloads are saved.

Set the directory at launch

For most scripts, configure the behavior in puppeteer.launch(). The directory should already exist, use an absolute path, and be writable by the Node.js process.

const puppeteer = require('puppeteer');
const fs = require('fs');
const path = require('path');

(async () => {
  const downloadPath = path.resolve(__dirname, 'downloads');
  fs.mkdirSync(downloadPath, { recursive: true });

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

  try {
    const page = await browser.newPage();
    await page.goto('https://example.com', { waitUntil: 'networkidle2' });
    await page.click('a.download');
  } finally {
    await browser.close();
  }
})();

policy: 'allow' permits downloads and uses the supplied directory. Set it before clicking a link, submitting a form, or running JavaScript that starts the download. The browser process, not the page’s current working directory, determines the destination.

Use an absolute, portable path

path.resolve() avoids ambiguity when a script is started from a different working directory. On Windows, let Node build the path instead of embedding unescaped backslashes:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const downloadPath = path.resolve(process.cwd(), 'downloads');

In containers, services, and CI, choose a directory mounted into the job or container and grant the account running Node write permission. A path that exists on your workstation may not exist in deployment.

Configure a separate browser context

If you create an isolated browser context, apply the download behavior to that context when your installed Puppeteer version supports BrowserContextOptions.downloadBehavior.

const puppeteer = require('puppeteer');
const fs = require('fs');
const path = require('path');

(async () => {
  const downloadPath = path.resolve(__dirname, 'context-downloads');
  fs.mkdirSync(downloadPath, { recursive: true });

  const browser = await puppeteer.launch();
  const context = await browser.createBrowserContext({
    downloadBehavior: {
      policy: 'allow',
      downloadPath,
    },
  });

  try {
    const page = await context.newPage();
    await page.goto('https://example.com');
    await page.click('a.download');
  } finally {
    await browser.close();
  }
})();

Contexts isolate cookies and local storage. They also give you a way to direct downloads from one automation session to a dedicated directory. The “next” API documentation can describe options that are not in an older released package, so check the type definitions and API reference installed with your version before using this form.

Choose the download policy

Policy Result Path requirement File naming
allow Permit page downloads downloadPath required Usually follows the server or browser-suggested name
allowAndName Permit page downloads downloadPath required Names files with download GUIDs
deny Reject downloads Not required No file is written
default Use Chrome’s default behavior when available; otherwise deny Not required by the protocol Determined by Chrome

Use allow when downstream code expects recognizable filenames. Use allowAndName when unique GUID-based names are preferable and your application records the mapping itself. The Chrome DevTools Protocol describes Browser.setDownloadBehavior as experimental, so behavior can depend on the Chrome and Puppeteer versions you deploy.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option

Wait for the file, not just the click

A click only starts a transfer. For reliable automation, wait until the expected file appears and its temporary download file has disappeared. Chrome commonly writes an incomplete file with a temporary extension while the transfer is active; do not process it until the final file exists.

const fs = require('fs');
const path = require('path');

async function waitForDownload(dir, timeoutMs = 60000) {
  const started = Date.now();
  while (Date.now() - started < timeoutMs) {
    const names = fs.readdirSync(dir);
    const finished = names.filter(name =>
      !name.endsWith('.crdownload') &&
      !name.endsWith('.tmp')
    );
    if (finished.length) return path.join(dir, finished[0]);
    await new Promise(resolve => setTimeout(resolve, 250));
  }
  throw new Error(`No completed download in ${dir}`);
}

For production jobs, use a unique directory per task, record the expected filename when possible, and verify size or content before moving the file to permanent storage. A download can finish with an HTTP error page, an access-denied response, or a file name different from the link text.

When the launch option is unavailable: CDP fallback

Older Puppeteer releases may not expose the required option in their launch or context types. Puppeteer’s Page API provides createCDPSession() for connecting to Chrome DevTools Protocol, whose browser-domain command is Browser.setDownloadBehavior.

const client = await page.createCDPSession();
await client.send('Browser.setDownloadBehavior', {
  behavior: 'allow',
  downloadPath: downloadPath,
});

This lower-level approach is version-sensitive. The protocol command is experimental, and a page session may not control the browser context you intended. Confirm the target session, Chrome build, and Puppeteer release in deployment. Prefer the documented Puppeteer option when it is available.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Do not confuse page downloads with Puppeteer’s cache

downloadPath controls files downloaded by pages. Puppeteer’s cacheDirectory controls where Puppeteer stores downloaded browser binaries. Changing the cache directory does not redirect PDFs, ZIP files, images, or other files initiated by a page.

Likewise, PUPPETEER_* installation and runtime settings are not a replacement for downloadBehavior. Puppeteer’s configuration guide notes that configuration files and environment variables are ignored by puppeteer-core; configure the browser or context explicitly in your code when using that package.

Common failures and fixes

The file is denied or no file appears

  • Set downloadBehavior before the action that triggers the transfer.
  • Confirm the policy is allow or allowAndName.
  • Check that the page belongs to the browser context where the behavior was configured.
  • Verify that the click actually starts a download rather than opening a new tab or returning an inline response.

“Download path” errors

  • Pass an absolute path.
  • Create the directory before launching or creating the context.
  • Check ownership and write permission for the Node process, especially in Docker, CI, systemd services, and serverless runtimes.
  • Ensure the path is not a file, read-only mount, or removed temporary directory.

The file has a GUID instead of its expected name

This is expected with allowAndName. Switch to allow if the server-provided suggested name matters, or capture the directory listing and associate the GUID-named file with the job that initiated it.

The context example is rejected by TypeScript or JavaScript

Your installed release may predate context-level downloadBehavior, or the option may be documented only in a next-version reference. Check the package’s installed types and release documentation. Use launch-level configuration where suitable, or validate the CDP fallback against your exact Chrome and Puppeteer versions.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #4
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • Brand: Wiley
  • Set of 2 Volumes
  • A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers

The script works locally but fails in deployment

  • Log the resolved path and process user.
  • Check the container or host mount and available disk space.
  • Use a per-job directory to avoid collisions between concurrent pages.
  • Clean completed files explicitly; browser download settings do not provide retention management.

Security and reliability considerations

Downloaded files are untrusted input. Store them outside directories served directly by your web server, restrict permissions, and scan or validate files before opening them. Do not let a URL or filename supplied by a remote user select an arbitrary filesystem path. Resolve paths beneath an approved root and reject traversal components when constructing per-job directories.

For parallel downloads, isolate jobs by context or directory and use unique identifiers. Set a timeout, observe the directory for completion, and handle navigation, authentication, and server-side rate limits separately from filesystem configuration. A permitted download can still fail because the page requires a login, presents a CAPTCHA, expires a signed URL, or returns an error response.

Or skip the browser setup

If your goal is to capture a rendered page rather than download a file from it, ScreenshotNeo provides a website screenshot API. One GET request returns PNG, JPEG, WebP, or PDF output; it is not a replacement for arbitrary file downloads, but it avoids maintaining a Puppeteer browser for screenshot jobs.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo documentation for all options. Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot; bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing result. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

FAQ

Where does Puppeteer save a download by default?

Do not rely on a default location for automated jobs. Set an explicit absolute downloadPath and make the directory writable.

Can I change the directory after a download starts?

Configure the browser or owning context before triggering the download. Changing settings after the transfer begins is unreliable; start a new job with the correct behavior instead.

Does this setting change where Puppeteer installs Chrome?

No. Browser installation caching uses cacheDirectory; page downloads use downloadPath.

Should I use launch-level or context-level configuration?

Use launch-level configuration for a simple script. Use context-level configuration when supported and when separate sessions need distinct download policies or directories.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Does Puppeteer wait for a download automatically?

No. The download behavior permits or denies the transfer, but your script should observe the destination and wait for a completed file before processing it.

Can a downloaded file be opened safely immediately?

Treat it as untrusted input. Validate the response and file, restrict permissions, and scan it before opening or serving it.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.