Free tools Windows power users keep installed
One-click scans. No signup required.
Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Firebase does not include a native HTML-to-PDF API. The dependable design is to send an authenticated request to server-side code, populate a controlled HTML template, render it with headless Chrome through Puppeteer or Playwright, and return the PDF or store it in Cloud Storage. You can run that renderer in Cloud Functions for Firebase or in a custom Cloud Run container, then optionally route traffic through Firebase Hosting.
The architecture that works
Treat Firebase as the authentication, API, hosting and storage layer—not as the PDF rendering engine. A typical request flows like this:
- Your app sends an authenticated request containing an invoice ID or other safe data reference.
- Server-side code verifies the user and loads the authoritative data. Do not trust totals, permissions or template variables supplied by browser JavaScript.
- The server fills a controlled HTML template and escapes inserted text.
- Puppeteer or Playwright launches headless Chrome and calls the browser’s PDF function.
- The endpoint either streams the PDF response or writes it to Cloud Storage and returns an authorized download URL.
Google Cloud’s Cloud Run browser-automation guidance explicitly lists headless Chrome for creating PDFs or screenshots of web pages and identifies Puppeteer and Playwright as high-level control libraries. The PDF is therefore produced by browser rendering in your backend, not by Firebase Hosting.
Choose Cloud Functions or Cloud Run
Both backends can serve an HTTPS endpoint, but they suit different operational needs.
#1 Best Overall
| Decision point | Cloud Functions for Firebase | Cloud Run |
|---|---|---|
| Firebase integration | Closest integration with Firebase CLI, Authentication and event-driven functions. | Can still use Firebase services, but you deploy and operate a container. |
| Runtime control | Use a supported runtime and its execution constraints. | Select the base image, runtime, browser packages and operating-system dependencies. |
| Browser dependency management | Convenient for a small Node.js function, but browser binary size and runtime limits must fit the supported environment. | Best when Chromium needs custom packages, fonts, launch flags or a non-Node runtime. |
| Firebase Hosting route | Hosting can route HTTPS requests to the function. | Hosting can route HTTPS requests to the service. |
| Hosting timeout | Requests routed through Hosting have a documented 60-second timeout. | The same 60-second Hosting route timeout applies. |
When Functions is the better start
Choose Cloud Functions when your application is already organized around Firebase CLI deployments, the renderer is modest, and your supported runtime can accommodate the browser dependency. The Firebase getting-started material states that production deployment requires the Blaze plan, so confirm the current plan, runtime support and limits before deploying.
When Cloud Run is the better fit
Use Cloud Run when you need a custom container, browser binaries, OS-level libraries, predictable fonts, a runtime other than Node.js, or finer control over memory and concurrency. Containerizing Chromium also makes it easier to reproduce the same environment locally and in production.
Do not hide long jobs behind Hosting
A large document, slow remote asset or cold browser can exceed the documented 60-second Firebase Hosting route timeout. For those jobs, call the backend directly where appropriate or create an asynchronous job: accept the request, render in the background, save the PDF, and notify the client when an authorized download is ready. Verify the direct service’s current limits rather than assuming the Hosting limit applies—or does not apply—to every route.
Build a secure HTML template
Keep templates under your control
Store templates with your server code or in a private, versioned location. Do not accept arbitrary HTML, CSS, file paths or destination URLs from an untrusted client. A renderer that can fetch arbitrary URLs may be abused to probe internal services or exfiltrate data.
Validate and escape values
Accept an identifier such as invoiceId, load the invoice on the server, check authorization, and derive the amount, customer and line items there. Escape text before inserting it into HTML. If a field genuinely supports rich markup, sanitize it with a narrowly defined allowlist instead of concatenating raw input.
Rank #2
Make assets available to Chrome
Fonts, logos, images and stylesheets must be reachable from the rendering environment. Prefer bundled or authenticated assets over fragile third-party URLs. Wait until fonts and images have loaded before exporting; otherwise the PDF can contain fallback fonts, blank image boxes or shifted page breaks.
Minimal Cloud Functions implementation
The following example is an implementation pattern, not a turnkey official Firebase recipe. It uses an HTTPS function, a server-side template, and Puppeteer. Pin compatible package versions and verify the currently supported Node.js runtime before deployment.
const { onRequest } = require("firebase-functions/v2/https");
const puppeteer = require("puppeteer");
const escapeHtml = require("escape-html");
exports.invoicePdf = onRequest(async (req, res) => {
if (req.method !== "POST") return res.status(405).send("POST required");
// Verify Firebase Authentication here; reject unauthenticated callers.
const { invoiceId } = req.body || {};
if (!/^[A-Za-z0-9_-]{1,80}$/.test(invoiceId || "")) {
return res.status(400).send("Invalid invoiceId");
}
// Load and authorize the invoice from your database.
const invoice = await loadAuthorizedInvoice(invoiceId, req);
if (!invoice) return res.status(404).send("Not found");
const rows = invoice.items.map(item =>
`<tr><td>${escapeHtml(item.name)}</td>` +
`<td>${escapeHtml(String(item.quantity))}</td>` +
`<td>${escapeHtml(item.totalFormatted)}</td></tr>`
).join("");
const html = `<!doctype html><html><head>
<meta charset="utf-8">
<style>@page { size: A4; margin: 18mm; } body { font: 12pt Arial; }</style>
</head><body>
<h1>Invoice ${escapeHtml(invoice.number)}</h1>
<p>${escapeHtml(invoice.customerName)}</p>
<table>${rows}</table>
</body></html>`;
const browser = await puppeteer.launch({ args: ["--no-sandbox", "--disable-setuid-sandbox"] });
try {
const page = await browser.newPage();
await page.setContent(html, { waitUntil: "networkidle0" });
await page.evaluate(() => document.fonts.ready);
const pdf = await page.pdf({ format: "A4", printBackground: true, preferCSSPageSize: true });
res.set("Content-Type", "application/pdf");
res.set("Content-Disposition", `attachment; filename="invoice-${invoice.number}.pdf"`);
return res.status(200).send(pdf);
} finally {
await browser.close();
}
});
In production, replace loadAuthorizedInvoice with your database access and Authentication checks, keep secrets in the server environment, and consider writing large PDFs to Cloud Storage instead of holding every result in the response process.
Cloud Run container considerations
A Cloud Run service packages your application and Chromium into an image. Choose a maintained Node.js or other base image, install the browser dependencies required by your chosen automation library, and expose an HTTP server on the port supplied by the platform. Set memory and concurrency from measurements on your actual templates. A browser per request is simple but expensive; a carefully managed browser or page pool can improve throughput, while excessive concurrency can exhaust memory. Close pages and browsers on success, timeout and error paths.
PDF quality and template details
Page size, margins and breaks
Use CSS @page rules and the PDF options together. Decide whether CSS controls the paper size (preferCSSPageSize) or the API option does, then test both portrait and landscape templates. Add explicit page-break rules around invoices, tables and signatures. Long tables need repeated headers and a policy for rows that cannot be split.
Rank #3
Images and fonts
Use absolute, reachable asset URLs or inline small assets. Wait for network idle and document.fonts.ready, but do not rely on network idle alone when an application keeps analytics connections open. A local font package or a stable internal asset endpoint avoids changes caused by external font providers.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Dynamic pages
If the template is a client-rendered application, wait for a specific readiness selector rather than exporting immediately after navigation. For a static server-generated template, page.setContent is usually simpler and avoids exposing an unnecessary public URL to the renderer.
Returning or storing the file
Streaming is appropriate for small, interactive documents. For lengthy reports or jobs that may exceed a request timeout, create a job record, render asynchronously, save the PDF to a private Cloud Storage bucket, and return a short-lived authorized download path. Never make a bucket public merely to simplify downloads. Include a document ID and status endpoint so clients can retry safely without creating duplicate invoices.
Or skip the browser setup
ScreenshotNeo is a hosted screenshot and PDF API. It can accept a URL and return a PDF, so it is useful when your HTML template is already served from an authenticated or signed route and you do not want to package Chromium yourself. It also removes cookie banners, newsletter popups and chat widgets before capture; bot checks, blank pages and failed loads are not billed; and its MCP server lets AI agents use take_screenshot, get_page_info and capture_pdf.
For a public or signed template URL, call the API as documented at ScreenshotNeo’s documentation:
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorscurl -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}`);
The examples use the service’s default image output; request PDF output and other capture options through the documented parameters. ScreenshotNeo’s Free plan includes 1,000 shots per month with no card, and paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try the hosted route.
Rank #4
Troubleshooting
The function deploys but Chromium will not start
The browser binary or required OS libraries do not match the runtime. Pin a compatible automation-library version, use the supported browser installation method, or move to a Cloud Run image that explicitly installs Chromium and its dependencies. Avoid adding launch flags blindly; use only those required by the container’s security model.
The PDF is blank or missing data
The export ran before client rendering completed, the template threw a browser error, or data was inserted into the wrong document. Log the response status, capture browser console errors, wait for a readiness selector, and verify the generated HTML in a local browser.
Images or fonts are missing
Check that the renderer can resolve every URL without browser-only credentials, wait for image completion and fonts, and inspect network failures. Bundle critical assets or expose them through a controlled signed endpoint.
Recommended Free Tools
Requests time out at 60 seconds
If the request passes through Firebase Hosting, the documented route timeout is 60 seconds. Reduce asset and template work, call the backend directly where suitable, or switch to an asynchronous job that stores the completed PDF.
Users can alter invoice totals
Do not use client-submitted totals as authoritative values. Send an ID, re-read the record server-side, authorize it for the caller, calculate totals on the server and escape every inserted text value.
Best Value
Retries create duplicate documents
Give each request an idempotency key or deterministic job ID. Record the rendering state before starting, reuse a completed object on safe retries, and clean up partial files after failures.
Testing and operating the renderer
- Test representative short and long documents, large tables, images, page breaks, Unicode characters and right-to-left text where applicable.
- Measure cold-start and warm-render times, memory consumption, browser crashes and concurrent requests on the exact templates you ship.
- Set explicit navigation, asset and overall job timeouts; log document IDs rather than sensitive customer data.
- Retry transient browser or storage failures with limits, but do not retry validation or authorization errors.
- Check current Firebase, Cloud Functions, Cloud Run and Puppeteer or Playwright runtime guidance before each production upgrade.
There is no universal per-document price or performance number for this workflow. Your bill and throughput depend on runtime, memory, invocation duration, storage and workload, so measure them with production-shaped documents instead of assuming a benchmark.
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 →Frequently Asked Questions
Can Firebase Hosting itself convert HTML into a PDF?
No. Hosting can serve files and route requests to a function or Cloud Run service; a server-side browser renderer must create the PDF.
Should I use Puppeteer or Playwright?
Both are documented high-level browser-control approaches for headless Chrome. Choose based on the browser version, APIs and packaging constraints that fit your selected runtime.
Do I need Cloud Storage for every PDF?
No. Stream small results directly. Storage is useful for asynchronous, large or repeatedly downloadable documents.
What plan is required to deploy production Cloud Functions?
The Firebase getting-started guidance states that production deployment requires the Blaze plan; verify current plan requirements before launch.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteQuick 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.

