Skip to content

Puppeteer alternative

Puppeteer Alternative for Managed Headless Chrome PDF Rendering with No Fleet

Puppeteer is an excellent library and a miserable service to operate. If you are here because the browser fleet keeps waking someone up, this page is the honest version of the trade off, including when you should just keep Puppeteer.

  • PDF and archive in one call
  • Plans from $49 a month
  • Your HTML stays yours

Short answer

The best Puppeteer alternative for PDF generation is a managed rendering API: you keep the same Chromium output quality, but the vendor runs the browser pool, patches it, and absorbs the memory leaks. Puppeteer itself is free; what costs money is the engineer who owns the fleet. Swap the library for one HTTP call when render reliability starts consuming on call time, or when you need a dated record of what you rendered, which no headless browser gives you.

Written and fact checked by the SitePDF team.

Puppeteer, wkhtmltopdf and a managed API, side by side

This is a comparison of operating models, not a benchmark. All three produce good PDFs on a good day. They diverge on who fixes it at 3am.

Puppeteer (self-hosted) wkhtmltopdf SitePDF API
Licence cost Free (Apache 2.0) Free (LGPL) Paid, from $49/mo
Rendering engine Current Chromium Old WebKit fork, archived upstream Managed current Chromium
Modern CSS and JS Full support Partial: flexbox and grid are unreliable Full support
Who runs the browser You do You do We do
Typical failure mode Memory leak, zombie processes, pool wedges Silently renders the page wrong Explicit error, no quota charged
Wait for late content You write it yourself Not really wait_for selector, built in
Timestamped archive of the render No No Yes, archive=true
Scales by Adding servers you manage Adding servers you manage Your plan's concurrency

Licence and engine facts are from each project's own documentation. wkhtmltopdf's upstream repository has been archived and its maintainers recommend moving to a Chromium based renderer, which is the honest reason it sits third here.

The bill for "free" is not zero, it is just not on the invoice

Puppeteer costs nothing to install. It costs a great deal to run. The pattern is so consistent it is almost a rite of passage: a single puppeteer.launch() in a request handler works beautifully in staging, then production traffic arrives and the box runs out of memory because browsers were never closed on the error path. So you build a pool. The pool wedges. So you add a health check and a reaper. Chrome ships a new major version and your page breaks in a way that reproduces on nobody's laptop. Someone patches a font package at 11pm because Arabic invoices are rendering as boxes.

None of that appears on a budget line. It appears as an engineer's week, repeatedly, and as the on call rotation nobody volunteers for. The library is free; the fleet is a product you did not mean to build and do not get paid for.

We wrote the full arithmetic in HTML to PDF library vs API, including the volumes at which self-hosting genuinely does win.

When you should keep Puppeteer, honestly

We are not going to pretend the API is always right. Keep the library when any of these are true. Your rendering needs are trivial and stable, a handful of simple templates that have not changed in a year. You already operate browser automation for other reasons, scraping or end to end tests, so the fleet exists whether or not you make PDFs with it. Your data cannot leave your network for regulatory reasons, and no vendor contract fixes that. Or your volume is so high that per render pricing genuinely exceeds the cost of servers plus the fraction of an engineer who tends them.

That last one is a real number, not a rhetorical one. Work it out before you buy anything. If self-hosting wins on your numbers, self-host.

We have done that arithmetic at the settings the serverless Chromium maintainers recommend. At 100,000 renders a month, Chromium on AWS Lambda costs about $10.44 of compute against roughly $140 on a managed browser plan, so the entire annual difference is worth 10 to 21 hours of engineering time. The full working, including Cloud Run, the free tier effect and the timeout trap that catches most teams, is in what Chrome on AWS Lambda really costs per PDF.

If you keep Puppeteer but stop hosting the browsers yourself, the managed Chrome market is the next thing to price, and its billing increments vary much more than its hourly rates do. We compared eight of those vendors on Browserbase pricing.

Playwright vs Puppeteer for PDF generation, and why switching libraries changes little

For PDF output the two libraries produce near identical files, because both drive Chromium and call the same print to PDF function. WebKit and Firefox headless do not expose the same capability, so a Playwright PDF is a Chromium PDF in practice. Anyone who says one makes visibly better PDFs than the other from the same HTML is measuring noise. The real differences are in the API, browser management and language support:

PuppeteerPlaywright
PDF outputChromium print to PDFIdentical, same Chromium
Official languagesJavaScriptJavaScript, Python, .NET, Java
Waiting for contentwaitUntil: 'networkidle0' and waitForSelector, written out by handAuto waiting built into actions, plus networkidle and wait_for_selector
Browser managementDownloads a bundled Chromium on installPinned per release through playwright install
PDF from WebKit or FirefoxNoNo, Chromium only in practice
Who runs the browser fleetYouYou

Pick Playwright if your stack is Python, .NET or Java, because Puppeteer is JavaScript first and the community ports (PuppeteerSharp, pyppeteer) lag upstream. Pick Puppeteer if you are Node only and want the smallest, most established dependency. Set printBackground: true in either one, since Chromium drops background colors and images in print mode by default and that flag is the usual reason a PDF looks washed out next to the screen.

Now look at the last row. Switching libraries does nothing to the memory leaks, zombie processes, restart policies and concurrency queues described above, because the weight is in the browser, not the wrapper. If PDFs are a supporting feature (invoices after checkout, monthly statements, exported reports), the cheaper move is to stop running either one and send the same render as an HTTP call, with wait_for doing the job of waitForSelector.

Can you convert HTML to PDF without headless Chrome?

Yes, and there are exactly two honest ways to do it. They pull in opposite directions. The first is a pure code engine that draws the PDF from HTML and CSS with no browser at all: WeasyPrint in Python, dompdf and mPDF in PHP, OpenHTMLtoPDF in Java. These start instantly and fit a small serverless function, but they only support the CSS their authors implemented and none of them run JavaScript. WeasyPrint is the strongest, with flexbox, grid and paged media implemented; dompdf sits around CSS 2.1, so a Tailwind template collapses in it. A chart drawn client side simply does not appear, which is why WeasyPrint renders charts as empty boxes.

The second keeps browser grade rendering and moves the browser off your servers, by calling a hosted rendering API. Flexbox, grid, web fonts and JavaScript all behave, and it fits hosts that forbid launching a browser process. The trade is that your HTML, or a URL to it, is rendered by a third party, which is a data residency question to answer before you commit. A middle position exists too: run Gotenberg as its own container, so you still run Chrome but a crash takes down a pod rather than your API.

Your situationBest fit
Simple, static, print style HTML you fully controlPure code engine (WeasyPrint, dompdf)
Real app pages, modern CSS or JavaScript, output must match ChromeHosted rendering API
Browser fidelity, but the data cannot leave your networkSelf hosted browser service (Gotenberg)
Serverless, or a host that will not launch a browserHosted rendering API
You need a dated, verifiable record of each PDFHosted rendering API with archiving

There is no option that is both pure code and pixel identical to Chrome, because matching a browser is precisely what requires a browser. Decide whether you mean "without a heavy engine" or "without me babysitting a browser", and the right tool follows.

What you get by switching that Puppeteer cannot give you at all

Output quality is a wash. It is the same engine, so the PDF looks the same. What changes is everything around the render.

The pool, the patching and the page at 3am become someone else's job. Failed renders return an explicit error with the HTTP status the page returned, instead of a blank PDF you discover three weeks later in a customer complaint. And the render can carry a record: archive=true stores a timestamped, immutable snapshot of the page under your account, retrievable by id for the life of your retention window. Headless Chrome has no concept of this, and bolting it on means building a second pipeline, a storage layer and a retention policy. Archiving is the half of the job Puppeteer was never going to do for you.

If a record is the reason you are reading this, the save webpage as PDF and screenshot API pages cover the capture side, and the PDF generator API covers document generation from your own templates.

Migrating off the fleet is a one function change

Almost every Puppeteer PDF integration reduces to one internal function: give it HTML or a URL, get back a buffer. Replacing the body of that function with an HTTP call is usually an afternoon, and you can run both paths side by side behind a flag while you compare output on real documents. Nothing about your templates changes, because the engine did not change.

Tiers are on the pricing page, and the seven tool comparison lists the other vendors worth pricing against us, because you should check.

The migration: the Puppeteer block on the left becomes one call. Same Chromium, none of the fleet.
// before: you own the browser, the pool and the leak
const browser = await puppeteer.launch({ args: ['--no-sandbox'] })
const page = await browser.newPage()
await page.goto(url, { waitUntil: 'networkidle0' })
await page.waitForSelector('.order-summary')
const pdf = await page.pdf({ format: 'A4' })
await browser.close()   // the line everyone forgets on the error path

// after: one call, plus a dated record you never had before
const res = await fetch('https://api.sitepdf.com/v1/render', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.SITEPDF_KEY}` },
  body: new URLSearchParams({
    url,
    wait_for: '.order-summary',
    format: 'A4',
    archive: 'true',
  }),
})
const { pdf_url, archive } = await res.json()

This is the documented API call shape. Full request and response walkthrough.

Questions about this job

Is Playwright better than Puppeteer for PDF generation?
For the PDF itself, no. Both drive the same Chromium print to PDF function, so the files come out near identical. Playwright wins on official Python, .NET and Java bindings, auto waiting and pinned browser versions; Puppeteer is the smaller dependency for Node only teams. Neither removes the cost of running the browser fleet.
Can I convert HTML to PDF without headless Chrome?
Yes, in two ways. A pure code engine such as WeasyPrint or dompdf renders without any browser but supports only part of modern CSS and runs no JavaScript. A hosted rendering API keeps full Chromium fidelity while the browser runs on the vendor side, at the cost of sending your HTML to a third party.
What is the best alternative to Puppeteer for generating PDFs?
For most teams it is a managed rendering API, because it keeps Chromium output quality while removing the browser fleet you would otherwise operate. Puppeteer remains the right answer when your volume is very high, your rendering is simple and stable, or your data legally cannot leave your network.
Why does Puppeteer leak memory in production?
Almost always because browser or page instances are not closed on every code path, especially when a render throws. Each orphaned Chromium process holds its memory until the box runs out. The standard fixes, a pool with health checks and a reaper process, work, but they are infrastructure you now own and maintain.
Is wkhtmltopdf still a good option in 2026?
For simple, static, print oriented documents it still works and costs nothing. But it renders with an old WebKit fork, its upstream repository is archived, and modern CSS such as flexbox and grid is unreliable. If your templates use current CSS or any JavaScript, choose a Chromium based renderer instead.
Will my PDFs look different if I switch from Puppeteer to an API?
Not materially, because the rendering engine is the same Chromium. The practical differences are operational: renders run on a warmed managed pool, failures come back as explicit errors rather than blank files, and each render can be archived as a timestamped snapshot.
How hard is it to migrate off Puppeteer?
Usually an afternoon. Most integrations funnel through one function that takes HTML or a URL and returns a PDF buffer; you replace its body with an HTTP request. Run it behind a feature flag alongside the old path and diff the output on real documents before you cut over.

Index

More PDF and archiving tools

Get started

Render and archive with one API call

A pixel perfect PDF and a dated copy of the page from the same request. Plans from $49 a month, half price billed yearly.

HTML to PDF, in your browser

Buy the plan