Generate the PDF inside an HTTP-triggered Firebase Function, then return Puppeteer’s Uint8Array as an HTTP attachment. The essential sequence is: validate the request, launch a Chromium binary available in the deployed function, render trusted content, call page.pdf(), send the bytes with PDF headers, and close the browser in a finally block.
Architecture: render, return, and download
An HTTP function is a good fit when a user or application needs a PDF immediately. The function receives parameters, renders a page, and sends the resulting bytes. Firebase requires every HTTP handler to finish with send(), redirect(), or end(); an unended response can leave clients waiting.
The example below uses Firebase Functions v2 and Node.js. Firebase currently lists Node.js 20 and 22 as supported and Node.js 18 as deprecated. Node.js 14 and 16 deployments are disabled after their decommissioning in early 2025. Confirm the runtime selected in your project before deploying.
1. Create the function project
Install the required packages
In an initialized Firebase project, install the Functions SDK and Puppeteer:
Recommended Free Tools
#1 Best Overall
- Engineered for convenience – This new Brother Monochrome Laser Printer is conveniently equipped with a flatbed scan glass for quick copying and scanning. Mobile Device Compatibility AirPrint, Google Cloud Print 2.0, Brother iPrint and Scan, Mopria, Cortado Workplace
- Optimized for efficiency – Engineered with new features, the HL L2395DW laser printer (replacement for the HLL2380DW) and has been optimized for efficiency, allowing you to print up to 36 pages per minute(1)
- Faster, high quality prints: This monochrome laser printer is built with a 250 sheet paper capacity that helps improve efficiency due to less time spent refilling trays. It also handles both letter and legal sized paper. Power Source AC 120V 50/60Hz.Machine Noise (Ready/Printing): 30dB / 50dB
- Cloud based print & scan – Print from and scan to popular Cloud services directly from the 2.7" color touchscreen, including Dropbox, Google Drive, Evernote, OneNote, and more(4)
- Wireless printing & exceptional support – This printer’s simple to connect wireless technology allows you to submit print jobs from your laptop, smartphone, desktop, and tablets(2). The "Touch to connect" printing with NFC delivers added convenience(3).
npm install firebase-functions puppeteer
The standard puppeteer package downloads a compatible Chrome for Testing browser during installation. Your build must run the install step and preserve Puppeteer’s browser cache in the deployed artifact. If your package manager blocks install scripts, the browser may not be present.
Choose a supported runtime
Set the Functions runtime in functions/package.json, for example:
{
"engines": {
"node": "20"
}
}
Use source-code runtime options for memory, timeout, and CORS. The values in the complete example are starting points, not performance guarantees. Firebase documents HTTP and callable function timeouts up to 3,600 seconds; that is a ceiling, not an expected PDF-generation time.
2. Implement a PDF download endpoint
This handler renders a small HTML document supplied by the function itself. In production, authenticate callers and use an allowlist or trusted data source rather than accepting unrestricted URLs or HTML. A renderer that can fetch arbitrary addresses can be abused for server-side request forgery.
const { onRequest } = require('firebase-functions/v2/https');
const puppeteer = require('puppeteer');
exports.downloadPdf = onRequest({
memory: '1GiB',
timeoutSeconds: 120,
cors: ['https://your-frontend.example'],
}, async (req, res) => {
let browser;
try {
if (req.method !== 'GET' && req.method !== 'POST') {
res.status(405).set('Allow', 'GET, POST').send('Method not allowed');
return;
}
const title = typeof req.query.title === 'string'
? req.query.title.slice(0, 200)
: 'Example document';
browser = await puppeteer.launch({
// Add project-specific flags only when your deployed runtime requires them.
});
const page = await browser.newPage();
const html = `<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>${title.replace(/[&<>"']/g, '')}</title>
<style>
@page { size: A4; margin: 18mm; }
body { font-family: Arial, sans-serif; color: #222; }
h1 { font-size: 24px; }
</style>
</head>
<body>
<h1>${title.replace(/[&<>"']/g, '')}</h1>
<p>Generated by a Firebase HTTP function.</p>
</body>
</html>`;
await page.setContent(html, { waitUntil: 'networkidle0' });
const pdf = await page.pdf({
format: 'A4',
printBackground: true,
});
res.status(200)
.set('Content-Type', 'application/pdf')
.set('Content-Disposition', 'attachment; filename="document.pdf"')
.send(Buffer.from(pdf));
} catch (error) {
console.error('PDF generation failed', error);
if (!res.headersSent) res.status(500).send('PDF generation failed');
} finally {
if (browser) await browser.close();
}
});
page.pdf() returns a Promise<Uint8Array>. Converting it to a Node.js Buffer lets Express, which underlies the Firebase response object, send the binary body. Content-Type identifies the format, while Content-Disposition: attachment gives browsers a download filename.
3. Render real pages reliably
Navigate to a URL
For a page you control, replace setContent() with navigation and wait for the state your application needs:
await page.goto('https://your-site.example/report/123', {
waitUntil: 'networkidle0',
timeout: 60000,
});
await page.waitForSelector('#report-ready', { timeout: 30000 });
const pdf = await page.pdf({ format: 'A4', printBackground: true });
Do not treat networkidle0 as proof that asynchronous data is ready. A page can continue rendering after network activity settles; a specific readiness selector is more reliable. Keep outbound destinations restricted and validate every identifier used to construct a URL.
Rank #2
- FAST PRINT AND SCAN: The Brother MFC-L3710CW lets you get things done with up to 19 ppm print speed and scans up to 29 ipm in black and 22 ipm in color
- AFFORDABLE AND FLEXIBLE COLOR PRINTING: Affordably print professional quality, rich, vivid color documents with laser printer quality. The 250 sheet adjustable paper tray helps minimize refills and the manual feed slot handles varied printing needs
- 3.7” COLOR TOUCHSCREEN: Print from and scan to popular cloud apps directly from the 3.7" color touchscreen including Dropbox, Google Drive, Evernote, OneNote and more. Save time by creating custom shortcuts on the touchscreen for your most used features.
- PRINT AND CONNECT YOUR WAY: Print wirelessly from your desktop, laptop, smartphone and tablet with built-in wireless, and Wi-Fi Direct or connect locally to a single computer via USB interface.
- UNIT DIMENSIONS (WxDxH): 16.1” W x 18.7” D x 16.3” H
Control print and screen styles
Puppeteer’s API states that page.pdf() “Generates a PDF of the page with the print CSS media type.” If your design uses screen styles, call this before generating the PDF:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
await page.emulateMediaType('screen');
const pdf = await page.pdf({ printBackground: true });
Print rendering can alter colors. For color-sensitive documents, CSS using -webkit-print-color-adjust can request exact colors, although the final result still depends on the page and browser.
Useful PDF options
format: 'A4','Letter', or explicitwidthandheightcontrols paper dimensions.marginsets top, right, bottom, and left margins.landscape: truerotates the page.printBackground: trueincludes background colors and images.displayHeaderFooter,headerTemplate, andfooterTemplateadd repeating page furniture.pageRangeslimits output to selected pages.preferCSSPageSize: truehonors a document’s CSS@pagesize.
4. Select a Chromium packaging strategy
| Approach | Browser delivery | Advantages | Risks and maintenance |
|---|---|---|---|
puppeteer |
Downloads Chrome for Testing during installation | Simplest API and version pairing | Build scripts must run; cache and artifact size must be preserved |
puppeteer-core |
No browser download | Use a browser you manage or a remote endpoint | You must configure an executable path or connection and maintain compatibility |
@sparticuz/chromium plus puppeteer-core |
Packages a serverless Chromium binary; the README describes a binary over 50 MB and a chromium-min option with separately hosted assets |
Explicit packaging and launch control | Check Chromium/Puppeteer pairing, Linux architecture, permissions, and Firebase behavior; its turnkey compatibility statement covers supported AWS Lambda Node.js runtimes, not an official Firebase certification |
There is no single Firebase-certified Puppeteer/Chromium version pair established for every project. Validate the deployed artifact, architecture, executable permissions, fonts, browser launch, and generated output in the actual Firebase environment. Local success does not prove deployment success.
Google Cloud Functions cache behavior
Puppeteer’s troubleshooting guidance says the Google Cloud Functions Node.js runtime has system packages needed for headless Chrome and recommends placing Puppeteer’s browser cache under node_modules when a cached build prevents the install process from running. Treat this as deployment troubleshooting, and verify the result with your current build pipeline.
5. Deploy and call the endpoint
- From the project root, run
firebase deploy --only functions:downloadPdf. - Copy the deployed HTTPS URL shown by the Firebase CLI.
- Open the URL in a browser, or request it with a client that saves binary data.
curl -L "https://REGION-PROJECT.cloudfunctions.net/downloadPdf?title=Invoice"
-o invoice.pdf
For a frontend using fetch, read the response as a Blob and create an object URL. Cross-origin browser calls require an explicit CORS allowlist; Firebase HTTP functions have no CORS policy by default. A command-line request can work while a browser request fails because of CORS.
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 reinstallconst response = await fetch(functionUrl + '?title=Invoice');
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const blob = await response.blob();
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = 'invoice.pdf';
link.click();
URL.revokeObjectURL(link.href);
6. Decide between direct bytes and stored files
| Pattern | Best when | Trade-offs |
|---|---|---|
| Send bytes directly | The caller needs one immediate download | Caller waits for rendering; retries repeat generation; very large responses increase request and memory pressure |
| Generate and store a file | A PDF must be reused, shared, or downloaded later | Requires storage permissions, cleanup, access control, a durable-link design, and a separate status/retry path |
The reviewed APIs do not define a universal PDF-size threshold for switching to storage. Base that decision on observed document size, generation latency, concurrency, retry behavior, and whether a reusable link is required.
7. Resource, concurrency, and security considerations
- Memory and CPU: Chromium is resource-intensive. A 1 GiB allocation and 120-second timeout are illustrative starting values; tune them from your deployed workload. Firebase’s second-generation CPU defaults vary with memory, which can affect cost.
- Browser lifecycle: Always close the browser in
finally. Leaked processes consume memory and can exhaust concurrent instances. - Authentication: Verify Firebase Auth, signed tokens, or another authorization mechanism before generating sensitive documents.
- Input controls: Limit HTML size, URL schemes, redirects, navigation time, and destination hosts. Block access to internal services when accepting any caller-controlled address.
- Fonts and assets: Install or package fonts required by the document and make external assets reachable from the runtime; missing fonts change pagination.
- Observability: Log request IDs, rendering duration, browser-launch failures, and page errors without logging secrets or full private document contents.
8. Troubleshooting deployed PDFs
“Could not find Chrome” or an executable-path error
Cause: the install script did not run, the cache was omitted, or puppeteer-core has no configured browser. Fix: confirm the package is installed during the Firebase build, inspect the deployed artifact, keep the browser cache under node_modules when appropriate, or provide an explicit executable path/remote browser for puppeteer-core.
Rank #3
- Create a mix using audio, music and voice tracks and recordings.
- Customize your tracks with amazing effects and helpful editing tools.
- Use tools like the Beat Maker and Midi Creator.
- Work efficiently by using Bookmarks and tools like Effect Chain, which allow you to apply multiple effects at a time
- Use one of the many other NCH multimedia applications that are integrated with MixPad.
Launch fails only after deployment
Cause: local and deployed Linux architecture, permissions, libraries, or browser versions differ. Fix: log the deployed runtime, verify the executable is present and executable, and test the exact package pairing in a deployed function. Do not assume an AWS Lambda-oriented Chromium package is Firebase-certified.
The function times out
Cause: slow navigation, never-ending requests, blocked assets, or insufficient resources. Fix: set explicit navigation and selector timeouts, wait for a deterministic readiness marker, remove unnecessary third-party requests, and increase timeout or memory based on measured behavior. The 3,600-second Firebase maximum is not a target.
Free tools Windows power users keep installed
One-click scans. No signup required.
The PDF is blank or missing data
Cause: capture occurred before client-side rendering completed, or the page required authentication/assets unavailable to Chromium. Fix: wait for a content selector, set cookies or headers deliberately, inspect page console and request failures, and ensure the data endpoint is reachable from the function.
Colors, backgrounds, or page breaks differ
Cause: print media CSS, omitted backgrounds, missing fonts, or unconstrained content. Fix: choose emulateMediaType('screen') when appropriate, set printBackground: true, define @page and break rules, and package the required fonts.
Browser download works with curl but not in the web app
Cause: CORS is disabled by default. Fix: configure cors with only the exact frontend origins, then handle non-2xx responses before reading the Blob.
Requests hang or consume all instances
Cause: browsers are not closed after exceptions, or unbounded concurrent jobs are accepted. Fix: retain the finally close path, authenticate and rate-limit callers, cap document complexity, and consider a queued generation-and-storage workflow for bursty workloads.
Or skip the browser setup
ScreenshotNeo provides a website screenshot API and MCP server when you need an image or PDF of a URL without packaging Chromium in Firebase. Its clean-shot workflow accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.
For a direct PDF or screenshot request, see the ScreenshotNeo API documentation:
Rank #4
- Save money by using PDF Fusion to view over 100 file formats without having to purchase additional software
- Merge incompatible files quickly and easily by dragging and dropping in PDF Fusion to create a new PDF documents
- Save time with PDF Fusion's editing tools to reuse the content from existing documents without starting from scratch
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}`);
Every plan includes the features, including full-page capture, element selection, device and retina settings, PDF paper controls, custom CSS and JavaScript, waits, request blocking, headers, cookies, geolocation, caching, signed links, webhooks, bulk capture, usage reporting, and an OpenAPI specification. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
FAQ
Does Puppeteer return a file path from page.pdf()?
No. It returns PDF bytes as a Uint8Array; write those bytes to storage or send them in the HTTP response.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesCan I use screen CSS without changing the page?
Yes. Call page.emulateMediaType('screen') immediately before page.pdf().
Is a 3,600-second timeout necessary for PDF generation?
No. It is Firebase’s documented HTTP-function ceiling. Set a timeout appropriate to your document and fail predictably when navigation or rendering exceeds it.
Frequently Asked Questions
Does Puppeteer return a file path from page.pdf()?
No. It returns PDF bytes as a Uint8Array; write those bytes to storage or send them in the HTTP response.
Can I use screen CSS without changing the page?
Yes. Call page.emulateMediaType(‘screen’) immediately before page.pdf().
Is a 3,600-second timeout necessary for PDF generation?
No. It is Firebase’s documented HTTP-function ceiling. Set a timeout appropriate to your document and fail predictably when navigation or rendering exceeds it.
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.

