Serverless HTML to PDF, without smuggling Chromium into your function
Everyone hits the same wall: Puppeteer works on your laptop, then fails in Lambda — the browser is bigger than the deployment limit, the runtime is missing libraries, and every cold start boots Chrome from scratch. The @sparticuz/chromium workaround trades that for version-pinning churn. Or: keep the browser out of your function entirely and make one HTTP call.
A Lambda handler with no layers
No puppeteer-core, no Chromium layer, no Docker image. The function stays small enough to upload from the console.
// AWS Lambda handler — Node.js 20 runtime, zero Chromium
export const handler = async (event) => {
const res = await fetch("https://api.pdfkitt.dev/v1/convert", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PDFKITT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
html: event.html,
options: { page_size: "A4" },
}),
});
return {
statusCode: 200,
headers: { "Content-Type": "application/pdf" },
body: Buffer.from(await res.arrayBuffer()).toString("base64"),
isBase64Encoded: true,
};
};When bundling Chromium still makes sense
If you need full browser automation in the function — crawling, screenshots of authenticated sessions, interaction — @sparticuz/chromium or a container image is the way. For plain HTML-to-PDF, moving rendering out of the function removes the size limit, the dependency churn, and the browser cold start in one move. The Node.js guide shows the same call in a regular backend, or jump to the API docs.
FAQ
Why does Puppeteer break on AWS Lambda?
The bundled Chrome build is 170-280 MB, which blows through Lambda's 50 MB zipped / 250 MB unzipped limits, and Lambda's minimal Linux lacks the shared libraries and fonts Chrome expects. The standard workaround - puppeteer-core plus @sparticuz/chromium - works, but the pinned Chromium/Puppeteer version pairs churn, and every cold start pays for booting a browser.
What about Vercel functions?
Same shape of problem: serverless function size limits and no system Chromium. Teams ship @sparticuz/chromium there too, with the same version-pinning fragility. Calling an external render API keeps your function at kilobytes and works on every plan.
Can Cloudflare Workers generate PDFs at all?
Workers cannot run Chromium in the worker itself - there is no process or filesystem. Options are Cloudflare's own Browser Rendering service or an external API. A plain fetch to pdfkitt works from any Worker with no bindings or paid add-ons.
Doesn't an external API just move the cold start?
pdfkitt keeps a warm Chromium pool - the browser is already running when your request arrives, with a ~680 ms median render. Your function stays tiny, so its own cold start stays fast too. You trade a per-invocation browser boot for a network hop.
Does it execute JavaScript in my HTML?
Not by default. Static HTML and CSS render as-is. If your page needs its scripts to run, set javascript: true in the request and the page runs in sandboxed Chromium before capture, with a 30-second cap per render.
Is my HTML stored anywhere?
No. HTML is processed in memory to render the PDF and is never written to storage or logs. Only request metadata such as timing and status is recorded.
Try pdfkitt free — 1,000 PDFs/month, no credit card
Get an API key and send your first PDF in minutes.