What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Pass an absolute filename in the path option to page.pdf(), then await the call before closing the browser: await page.pdf({ path: '/absolute/path/output.pdf' }); Puppeteer resolves a relative path from the process current working directory, so an absolute path prevents the output location from changing when your application is launched from another directory. If you omit path, Puppeteer returns PDF bytes but does not create a file.
The shortest working example
This CommonJS script launches Chromium, loads a page, creates the destination directory, and writes a PDF to an absolute path:
const puppeteer = require('puppeteer'); const fs = require('node:fs/promises'); const path = require('node:path'); (async () => { const browser = await puppeteer.launch(); try { const page = await browser.newPage(); await page.goto('https://example.com', { waitUntil: 'networkidle2' }); const outputPath = process.platform === 'win32' ? 'C:\reports\example.pdf' : '/tmp/reports/example.pdf'; await fs.mkdir(path.dirname(outputPath), { recursive: true }); await page.pdf({ path: outputPath }); console.log(`Wrote ${outputPath}`); } finally { await browser.close(); } })();
The important line is page.pdf({ path: outputPath }). The directory creation is separate: Puppeteer can write the file only when the parent directory exists and the Node process has permission to write there.
How Puppeteer interprets the PDF path
The PDFOptions reference documents that a relative path is resolved against the current working directory. That directory is determined by how the process was started, not necessarily by the directory containing your JavaScript file.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstall#1 Best Overall
- EDIT text, images & designs in PDF documents. ORGANIZE PDFs. Convert PDFs to Word, Excel & ePub.
- READ and Comment PDFs – Intuitive reading modes & document commenting and mark up.
- CREATE, COMBINE, SCAN and COMPRESS PDFs
- FILL forms & Digitally Sign PDFs. PROTECT and Encrypt PDFs
- LIFETIME License for 1 Windows PC or Laptop. 5GB MobiDrive Cloud Storage Included.
Value passed to path |
Result | Typical use |
|---|---|---|
'/var/reports/site.pdf' |
Absolute POSIX path | Linux, macOS, containers and CI runners |
'C:\reports\site.pdf' |
Absolute Windows path | Windows services or scheduled tasks |
'reports/site.pdf' |
Relative to the process current working directory | Only when that working directory is deliberately controlled |
No path property |
No file is written; the method returns PDF data | When your code will upload or store the bytes itself |
Use the path syntax of the host operating system. A path that is absolute on one operating system may be invalid on another, so avoid hard-coding Windows drive letters in a Linux deployment or POSIX separators in a Windows-only script.
Constructing an absolute path safely
Fixed deployment locations
For a known location, write the complete path directly and keep it in configuration rather than scattering it through the application:
const outputPath = '/srv/my-service/output/invoice.pdf'; await page.pdf({ path: outputPath });
On Windows, use a drive-qualified path:
const outputPath = 'C:\service-data\output\invoice.pdf'; await page.pdf({ path: outputPath });
Paths based on the current directory
path.resolve() turns a relative expression into an absolute one, but its result still depends on the current working directory when the expression is relative:
const path = require('node:path'); const outputPath = path.resolve('output', 'site.pdf'); console.log(outputPath);
This is useful for a command-line program whose working directory is intentionally fixed. It is not equivalent to a location anchored to the source file. If a process manager, test runner or container starts Node elsewhere, the resolved filename changes. For a stable deployment target, begin with an absolute base directory or obtain one from an environment variable that contains an absolute path.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Creating a unique filename
Build the filename with Node’s path utilities rather than concatenating separators manually:
Rank #2
- Edit PDFs with Ease. Modify text, images, and layouts directly within your PDF documents.
- Convert & Organize. Export PDFs to Word, Excel, or ePub, and organize files with ease.
- Read & Annotate. Enjoy intuitive reading modes and powerful tools to comment, highlight, and mark up PDFs.
- Create & Manage PDFs. Create new PDFs, combine multiple files, scan documents, and compress for easy sharing.
- Fill & Sign Forms. Complete forms and digitally sign documents with secure e-signature tools.
const path = require('node:path'); const id = 'order-1042'; const outputPath = path.join('/var/lib/reports', `${id}.pdf`);
When you accept a user-supplied name, validate it before joining it to the output directory. Reject path traversal such as ../ and avoid allowing a caller to replace arbitrary files.
A complete production-shaped flow
- Choose an absolute destination. Decide whether the file belongs in a local temporary directory, a durable application directory or a mounted volume.
- Create the parent directory. Use
fs.mkdir(directory, { recursive: true })before calling Puppeteer. This handles a missing directory but does not grant permissions that the operating-system user lacks. - Navigate and wait for the page state you need. The PDF captures the rendered page, so load the URL and wait for the content your application requires.
- Call and await
page.pdf(). Do not report success, upload the file or close the browser until the promise has resolved. - Close the browser in a
finallyblock. This releases Chromium even when navigation or PDF generation throws.
const puppeteer = require('puppeteer'); const fs = require('node:fs/promises'); const path = require('node:path'); async function savePdf(url, outputPath) { if (!path.isAbsolute(outputPath)) throw new Error(`Expected an absolute path, got: ${outputPath}`); await fs.mkdir(path.dirname(outputPath), { recursive: true }); const browser = await puppeteer.launch(); try { const page = await browser.newPage(); await page.goto(url, { waitUntil: 'networkidle2' }); await page.pdf({ path: outputPath }); } finally { await browser.close(); } } savePdf('https://example.com', '/var/reports/example.pdf').catch(error => { console.error(error); process.exitCode = 1; });
The explicit path.isAbsolute() check turns a configuration mistake into a clear error instead of silently placing the PDF under an unexpected working directory.
Saving bytes instead of letting Puppeteer write the file
The Page.pdf() API documentation specifies a Promise<Uint8Array> return value. If your next operation is an object-storage upload, database write or HTTP response, omit path and consume those bytes directly:
const pdfBytes = await page.pdf(); await fs.writeFile('/var/reports/example.pdf', pdfBytes);
This gives your code control over the subsequent storage operation. It is a different workflow from passing path; without that property Puppeteer itself does not create a disk file.
Print CSS, screen CSS and color behavior
PDF generation uses the page’s print CSS media type by default. A responsive layout can therefore differ from what you see in a normal browser tab. If the PDF should use screen styles, call page.emulateMediaType('screen') before page.pdf():
Rank #3
- Create and edit PDFs. Collaborate with ease. E-sign documents and collect signatures. Get everything done in one app, wherever you go.
- Edit text and images without jumping to another app.
- E-sign documents or request e-signatures on any device. Recipients don’t need to log in to e-sign.
- Convert PDFs to editable Microsoft Word, Excel, or PowerPoint documents.
- Share PDFs for collaboration. Commenting features make it easy for reviewers to comment, mark up, and annotate.
await page.emulateMediaType('screen'); await page.pdf({ path: '/var/reports/screen-styled.pdf' });
The API also notes that PDF rendering modifies colors for printing by default. When exact on-screen colors matter, use the CSS property -webkit-print-color-adjust in the page’s stylesheet and choose the value appropriate for your design. Treat this as a rendering decision, not a path setting.
Puppeteer’s PDF guide demonstrates awaiting page.pdf() and says font loading is awaited by default. You still need to await the method itself before treating the output as complete. See the official PDF generation guide for the documented sequence.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Troubleshooting absolute-path failures
The PDF appears in the wrong directory
Cause: The value was relative, or path.resolve() was called with a relative base while the process was started from an unexpected directory.
Fix: Log process.cwd(), check path.isAbsolute(outputPath), and configure a fully qualified path such as /srv/reports/file.pdf or C:\reports\file.pdf.
ENOENT: no such file or directory
Cause: The parent directory does not exist. The path option selects a filename; it does not create an arbitrary directory tree for you.
Rank #4
- Perfect Adobe Acrobat Pro alternative – lifetime license for Windows 10 and 11.
- EDIT text, images, pages, hyperlinks, designs in PDF documents. ORGANIZE PDFs.
- READ and Comment on PDFs – Intuitive reading modes & document commenting and mark up tools!
- CREATE, COMBINE, SCAN and COMPRESS PDFs.
- FILL forms & Digitally Sign PDFs. Work with Digital certificates
Fix: Run await fs.mkdir(path.dirname(outputPath), { recursive: true }) before PDF generation, and verify that the configured path is spelled correctly.
EACCES or permission denied
Cause: The operating-system account running Node cannot write to the destination directory, or a security policy blocks that location.
Fix: Select a writable directory, adjust its ownership or permissions according to your deployment policy, and test with the same service account used in production. Do not solve the problem by making an entire filesystem writable.
No file is created
Cause: The call omitted path. That is valid API usage, but it returns a Uint8Array rather than writing a disk file.
Fix: Supply { path: absoluteFilename }, or deliberately write the returned bytes with your own filesystem or storage code.
Recommended Free Tools
Best Value
- ALL-IN-ONE SOLUTION – read, edit, convert, merge and protect your PDF files
- MAXIMUM FUNCIONALITY – create interactive forms, compare PDFs, bates numbering, find and replace text or colors, convert documents, OCR engine, comment, highlight, fill out and print forms, document protection and others
- EASY TO INSTALL AND USE – well-structured user-interface, in-program instructions, free tech support whenever you need it
- GREAT VALUE FOR MONEY - why spend a fortune if you can have maximum functionality at a reasonable price - this also fits the requirements of companies very well
The process exits before the PDF is ready
Cause: The code did not await page.pdf(), or it closed the browser immediately after starting the operation.
Fix: Use await page.pdf(...) and close the browser only after that statement resolves. Put cleanup in finally so failures do not leak Chromium processes.
Layout or colors differ from the browser tab
Cause: The PDF uses print media and print color handling by default.
Fix: Call page.emulateMediaType('screen') when screen CSS is intended, and use -webkit-print-color-adjust when the stylesheet needs more exact color reproduction.
Operational and performance considerations
- Directory strategy: In containers and CI, write to a mounted or explicitly writable directory. A path can be syntactically absolute yet unavailable in the runtime filesystem.
- Concurrency: Give simultaneous jobs distinct filenames. Reusing one absolute filename lets concurrent jobs overwrite one another.
- Completion checks: Treat the awaited promise as the readiness boundary. If you need an additional application-level check, verify the expected file exists after the call returns.
- Memory: The API returns PDF bytes as a
Uint8Array; workflows that also retain or upload those bytes should account for that buffer while processing large documents. - Rendering choice: Decide print versus screen media before generation because the choice changes layout and styling, not just the output filename.
Or skip the browser setup
If you need a hosted website capture instead of maintaining Chromium and filesystem setup, ScreenshotNeo is a website screenshot API and MCP server. It accepts a URL and can return a clean PNG, JPEG, WebP or PDF. Before capture it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be disabled.
Only clean shots are billed. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits cost nothing, and each response reports the result through X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.
One GET request is enough (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
The service also supports full-page captures with lazy images loaded, CSS-selector element captures, dark mode, 12 device presets plus custom viewports, retina scale, PDF paper size, margins, landscape mode and page ranges, HTML/CSS-to-image, custom CSS and JavaScript, pre-capture clicks, hidden selectors, waits for selectors, delays or network idle, request and resource blocking, custom headers, cookies, user agents and Authorization, timezone and geolocation, transparent backgrounds, image resizing, configurable-TTL caching, signed links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API and an OpenAPI specification. Parameter names used by other screenshot APIs are accepted to ease migration.
There is a free allowance of 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; the published options are Growth at $15 for 15,000, Pro at $39 for 60,000, Scale at $99 for 250,000 and Business at $249 for 1,000,000, with two months free on yearly billing. Every feature is available on every plan. Create a free ScreenshotNeo account to try it without a card.
Quick Recap
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.

