wkhtmltopdf alternatives in 2026: what to use now that it's archived
The best wkhtmltopdf alternatives in 2026 are tools built on headless Chrome: a managed API like Transformy if you want rendering handled for you, or Puppeteer, Playwright, and Gotenberg if you would rather run the browser yourself. WeasyPrint covers the remaining case: print-focused documents rendered from Python without a browser at all.
This guide compares six realistic replacements (one managed API and five open-source routes) and ends with an option-by-option migration cheat sheet. Where a free library is the right call, we say so.
$ wkhtmltopdf --version
wkhtmltopdf 0.12.6 (with patched qt)
last release ········· June 2020
repo archived ········ January 2023
org archived ········· July 2024
CVE-2022-35583 ······· unpatched · CVSS 9.8
CVE-2020-21365 ······· unpatched · CVSS 7.5
active maintainers ··· 0
$
KEY TAKEAWAYS
- wkhtmltopdf is abandonware. Archived since January 2023, with CVE-2022-35583 (SSRF, CVSS 9.8) and CVE-2020-21365 (path traversal, CVSS 7.5) unpatched. Never feed it untrusted HTML.
- Headless Chrome is the natural successor. It renders flexbox, grid, web fonts, and JavaScript the way your browser does: all the things Qt WebKit never learned.
- Pick by who runs the browser. A managed API like Transformy trades a per-document fee for zero infrastructure. Puppeteer, Playwright, and Gotenberg are free but make browser operations your problem.
- No JavaScript in your templates? Consider WeasyPrint. For server-rendered invoices and reports in a Python stack, a paged-media engine can beat a browser.
- Migration is mostly renaming flags. Every common wkhtmltopdf option has a headless Chrome equivalent; the mapping below covers the ones that matter.
Why developers are replacing wkhtmltopdf
For over a decade, wkhtmltopdf was the default answer to "how do I turn HTML into a PDF on a server." One static binary, no browser to manage, and wrappers in every language (python-pdfkit, laravel-snappy, wicked_pdf). That era is over, for three concrete reasons.
It is unmaintained. The main repository was archived in January 2023, and the GitHub organization was archived in July 2024. There will be no more releases, which means no more security patches. Package managers have started pulling it: Homebrew disabled its cask in December 2024.
It has unpatched critical vulnerabilities. CVE-2022-35583 is a server-side request forgery rated CVSS 9.8: HTML passed to wkhtmltopdf can make requests against your internal network, including cloud metadata endpoints. CVE-2020-21365 is a path traversal (CVSS 7.5) that lets crafted HTML read local files into the rendered PDF. Neither will ever be fixed upstream.
Its rendering engine predates modern CSS. wkhtmltopdf embeds Qt WebKit, frozen around 2012-era web standards. CSS grid does not exist, flexbox is partial and buggy, web font loading is unreliable, and JavaScript support is roughly ES5. Every layout you build for it is a layout you could not have built the normal way.
Gotcha
"It still works for us" usually means the templates were written around the engine's bugs years ago. That is sunk cost, not stability, and it is the same reason teams dread touching those templates.
To be fair about the remaining safe zone: if wkhtmltopdf renders locked-down internal templates, with HTML your own code generates, on a network-isolated host, the CVEs are hard to exploit and you can migrate on your own schedule. The moment any user-supplied content reaches the HTML (names, addresses, comments, uploaded markup), that safe zone is gone.
What to look for in a replacement
Four questions narrow the field quickly.
What renders your HTML?
Headless Chrome gives you exactly what you see in the browser, JavaScript included. A paged-media engine like WeasyPrint skips JavaScript but implements print CSS (page numbers, running headers, @page rules) more deeply than browsers do.
Who operates the renderer?
A library runs in your process, a self-hosted service runs in your cluster, a managed API runs in someone else's. Browsers are heavy, leaky processes; owning them in production is a real ongoing cost, not a one-time setup.
What do paged documents need?
Headers and footers with page numbers, controlled page breaks, margins, and predictable fonts. Check how each option handles these; it is where HTML-to-PDF tools differ most.
What does it cost at your volume?
Free libraries cost engineering time; APIs cost per document. The crossover point depends on how many PDFs you generate and how much your team's time is worth.
The shortlist at a glance
| TOOL | TYPE | ENGINE | PRICING | BEST FOR |
|---|---|---|---|---|
| Transformy | Managed API | Headless Chrome | Free 100 docs/mo, then $99/mo for 10,000 | Browser-grade rendering without running browsers |
| Puppeteer | Library (Node.js) | Headless Chrome | Free (your infra) | Node.js teams with low volume or existing browser infra |
| Playwright | Library (Node.js, Python, Java, .NET) | Headless Chrome | Free (your infra) | Same as Puppeteer, with first-class bindings beyond Node.js |
| Headless Chrome CLI | Command-line tool | Headless Chrome | Free | One-off conversions and cron jobs, not production pipelines |
| Gotenberg | Self-hosted service | Chromium + LibreOffice | Free (your infra) | An HTTP API that keeps rendering in-house |
| WeasyPrint | Library (Python) | Own CSS paged-media engine | Free | Python stacks rendering JavaScript-free templates |
The managed option: Transformy
An API makes sense when PDFs matter to your product but running browsers does not. You send HTML or a URL, you get a PDF, and scaling, Chrome upgrades, and font handling stop being your pager's problem.
Transformy is an HTML-to-PDF API built on headless Chromium: send a URL or raw HTML in one authenticated POST and get a PDF back. Because the renderer is a real browser, flexbox, grid, web fonts, and print CSS come out the way you designed them, and JavaScript-rendered content is captured after it settles, so React, Vue, and client-side charts work without workarounds.
For wkhtmltopdf refugees, the document controls map cleanly: page_size, orientation, and margin parameters replace the CLI flags, and headers and footers are plain HTML snippets with {{page}}, {{pages}}, {{date}}, and {{title}} tokens, the same idea as --header-html, minus the quirks. Beyond parity, you get things wkhtmltopdf never had: async jobs with webhooks, delivery straight to your own S3/GCS/Azure bucket, idempotency keys for safe retries, and a test mode that renders real documents free with a watermark.
curl -X POST https://api.transformy.io/v1/pdf/chrome \
-H "Authorization: Bearer tfy_live_..." \
-H "Content-Type: application/json" \
-d '{
"html": "<html><body><h1>Invoice #1042</h1></body></html>",
"page_size": "a4",
"margin": "2cm",
"footer": { "html": "<span style=\"font-size:10px\">Page {{page}} of {{pages}}</span>" }
}' \
--output invoice.pdf
Pricing starts free (100 documents a month, no credit card), and the Pro plan is $99/month for 10,000 documents, then $0.01 per additional document. The honest trade-off: at very large volume, self-hosting Gotenberg or Puppeteer can be cheaper on paper, if you have the team to own it. And if your documents never needed JavaScript in the first place, read the WeasyPrint section before reaching for any browser-based tool, hosted or not.
Run it yourself: open-source options
If policy keeps rendering in-house, or your volume is small enough that an API fee feels silly, the open-source routes below work well. They are not lesser options; for the right situation, several of them are the recommendation.
Puppeteer and Playwright
Puppeteer and Playwright drive real headless Chrome from Node.js, and page.pdf() produces exactly what Chrome's print dialog would. For a handful of PDFs a day on infrastructure you already run, this is the right free answer. See our guides to Puppeteer PDF generation and Playwright HTML to PDF for working setups.
The costs show up at scale. Browsers leak memory and must be recycled; crashed renders leave zombie processes; concurrency means a browser pool with its own queue; serverless deploys fight Chromium's binary size and cold starts; and every Chrome release is now part of your test matrix. None of this is a reason to avoid Puppeteer; it is the reason "we'll run a browser ourselves" should be a deliberate decision, not a default. (We weigh those trade-offs in depth in our Puppeteer alternatives comparison.)
Tip
If you are on Python or PHP, driving Playwright works too, but the ecosystem's PDF tooling is strongest in Node.js. Our language guides for Python, PHP, Java, and C# walk through the realistic options per stack.
Headless Chrome from the command line
If your wkhtmltopdf usage is a shell script or a cron job, you may not need a library at all. Chrome itself converts pages from the command line:
chrome --headless --no-pdf-header-footer --print-to-pdf=report.pdf https://example.com/report
This is the most literal drop-in replacement on the list: one binary invocation in, one PDF out, using the same rendering engine as everything else here. The limits arrive fast, though: no per-request headers and footers with page numbers, coarse control over margins and waits, and spawning a full browser process per document does not survive contact with production volume. Treat it as the right answer for one-offs and scheduled reports, and as a signal to move up to Puppeteer, Gotenberg, or an API when the flags stop being enough.
Gotenberg
Gotenberg packages Chromium and LibreOffice behind a Docker container with an HTTP API: the middle ground between a library and a SaaS. Your application talks to it like it would talk to a managed API, but it runs in your cluster, under your compliance boundary, for free. You still own scaling, upgrades, and the occasional Chromium misbehavior, but Gotenberg's container does the ugly browser-lifecycle work for you. For self-hosting teams, it is the strongest wkhtmltopdf replacement on this list. (Weighing Gotenberg itself against other options? See our Gotenberg alternatives comparison.)
WeasyPrint
WeasyPrint is a Python library with its own rendering engine focused on CSS paged media: no browser, no JavaScript, and unusually good support for @page rules, running headers, and page numbering in pure CSS. If your PDFs come from server-rendered Django or Flask templates and never needed JavaScript, WeasyPrint is arguably a better fit than a browser: deterministic output, one pip install, no processes to babysit. It can also produce PDF/A and PDF/UA variants, which matters if archival or accessibility requirements are on your roadmap. Its limits are the flip side: no JavaScript at all, and rendering very large documents is slower than Chrome. Our WeasyPrint guide covers setup and the common font pitfalls, and our WeasyPrint alternatives comparison maps where its limits sit.
Migrating: mapping wkhtmltopdf options to headless Chrome
Most migrations are less dramatic than expected, because the concepts carry over. The renderer changes; the document model does not. Here is the mapping for the options that appear in almost every wkhtmltopdf command line, using Transformy's parameter names (Puppeteer's page.pdf() options are analogous):
- --page-size Letter
+ "page_size": "letter"
- --orientation Landscape
+ "orientation": "landscape"
- --margin-top 20mm
+ "margin": {"top": "20mm"}
- --header-html header.html
+ "header": {"html": "… {{page}} of {{pages}} …"}
- --print-media-type
+ "media": "print" # already the default
- --javascript-delay 2000
# not needed; JavaScript runs and settles before capture
- --no-background
+ "print_background": false
- --zoom 1.3
+ "zoom": 1.3
- --disable-smart-shrinking
+ "prefer_css_page_size": true # shrinking is gone; Chrome sizes honestly
Three things genuinely change, so budget a test pass with real documents:
- Smart shrinking is gone. wkhtmltopdf silently scaled pages to fit; Chrome does not. Layouts tuned against that scaling may need width fixes, usually deleting old workarounds, not adding new ones.
- Modern CSS now works.
break-inside: avoid, grid, and real web fonts behave correctly. Template hacks written for Qt WebKit (table-based layouts, float gymnastics) can finally be simplified. - Headers and footers are isolated documents. In Chrome-based renderers, header and footer HTML does not inherit the page's stylesheets, so inline the styles, as in the example above.
For a deeper flag-by-flag walkthrough of what your existing commands do, our wkhtmltopdf tutorial doubles as a translation reference, and the pdfkit guide covers the Python wrapper specifically.
wkhtmltopdf vs headless Chrome: what actually changes
Most of the alternatives above share one engine, so the real comparison is between the engines themselves. Here is where wkhtmltopdf's Qt WebKit and headless Chrome differ in practice:
| CAPABILITY | WKHTMLTOPDF (QT WEBKIT) | HEADLESS CHROME |
|---|---|---|
| CSS grid | Not supported | Full support |
| Flexbox | Partial, buggy (2012-era spec) | Full support |
| Web fonts | Unreliable loading, frequent fallbacks | Loads like the browser |
| JavaScript | Roughly ES5; fixed delays via --javascript-delay | Modern JS; rendering waits for the page to settle |
| Print CSS | Partial, with engine-specific quirks | Solid support for the common cases |
| Security patches | None since 2022; archived | Patched continuously with each Chrome release |
| Output stability | Frozen forever (a feature, if your templates never change) | Evolves with Chrome; rare minor shifts between versions |
| Binary footprint | One ~40 MB static binary | A full browser (hundreds of MB), which is the main reason managed APIs and Gotenberg exist |
Two honest observations from that table. First, wkhtmltopdf's frozen output is the one property some teams will genuinely miss: a PDF rendered today is byte-identical to one rendered in 2019. Chrome updates can shift a line break once in a while, which is why every serious pipeline, hosted or self-run, should pin and test engine upgrades rather than absorb them blindly. A managed API does that pinning and regression-testing for you; on Puppeteer it is another item on your checklist.
Second, the footprint row explains the whole landscape. Qt WebKit fit in one binary; Chrome does not. Everything in this article is a different answer to the question "who carries the browser": your process (Puppeteer, the Chrome CLI), your cluster (Gotenberg), a vendor (Transformy), or nobody (WeasyPrint, which sidesteps the browser entirely).
Which alternative fits your situation
You generate invoices and reports from your own templates, JavaScript-free, in Python
WeasyPrintfree, deterministic, PDF/A-capableYou want to replace one shell command with one shell command
Headless Chrome CLIuntil you outgrow its flagsYou already run Node.js and render fewer than a few hundred PDFs a day
Puppeteer or Playwrighton your existing infraRendering must stay inside your compliance boundary, but you want an HTTP API
Gotenbergin DockerPDFs are customer-facing, volume is growing, and nobody wants to own Chrome in production
Transformytest real templates on the free tierFrequently asked questions
Is wkhtmltopdf still safe to use in 2026?+
Only in a narrow case: internal templates, HTML generated entirely by your own code, on an isolated host. With any user-supplied content in the HTML, no: CVE-2022-35583 (SSRF) and CVE-2020-21365 (file disclosure) are unpatched and will stay that way, since the project is archived.
What is the best free alternative to wkhtmltopdf?+
Puppeteer or Playwright if you run Node.js and need JavaScript rendering; WeasyPrint if you are on Python and your templates are JavaScript-free; Gotenberg if you want a self-hosted HTTP service rather than a library. All three are actively maintained.
Will my existing wkhtmltopdf templates work with headless Chrome?+
Mostly, and usually better: modern CSS replaces old workarounds. The differences to test: smart shrinking no longer rescales overflowing layouts, and header/footer HTML must carry its own inline styles. Plan one test pass over your real document set.
What about pdfkit, laravel-snappy, and wicked_pdf?+
They are wrappers around the same archived wkhtmltopdf binary, so they inherit every problem above. Migrating means replacing the engine, not the wrapper; the wrapper code is usually the easy part to swap.
Why was wkhtmltopdf deprecated?+
Its engine, Qt WebKit, stopped being viable upstream, and the maintainers concluded a from-scratch rework was not realistic. The repository was archived in January 2023 and the GitHub organization in July 2024.
The bottom line
Ranking wkhtmltopdf alternatives only makes sense against your constraints. If documents are core to your product and you would rather not own browser infrastructure, a managed headless Chrome API is the pragmatic default. That is the gap Transformy is built for, and the free tier renders 100 documents a month with no credit card: enough to run your real templates through it this afternoon. If rendering must stay in-house, Gotenberg gives you the API shape without the SaaS; Puppeteer, Playwright, and the Chrome CLI cover the library-and-script end; and WeasyPrint remains the quietly excellent answer for JavaScript-free documents in Python.
The only wrong choice in 2026 is the status quo: an archived engine with critical CVEs is not a stable dependency; it is a deadline with an unknown date.