← shotpdf

wkhtmltopdf is archived. What to use instead.

26 August 2026

You have a service that turns HTML into PDFs, it has worked for years, and then someone opened the repository and saw the banner.

The wkhtmltopdf repository was archived by its owner on 2 January 2023 and is read-only. Nothing about your existing installation broke that day. But wkhtmltopdf is a wrapper around QtWebKit, and QtWebKit stopped moving long before that. The CSS you write now is not the CSS that engine implements, and no release is coming to close the gap. Migrating is the right call. The only question is what to migrate to.

Most answers to that question are a list of tools with a paragraph each. That list is not useful, because the tools are not really the variable.

The decision is about your CSS, not about the tools

Two different things get sold as "HTML to PDF", and they are not comparable.

One kind implements CSS itself: a library, in your process, that parses your markup and lays out pages. The other kind is a browser — a full engine that also happens to write PDF — driven from code.

Which one you need is decided by the document, not by your language or your deployment. Ask one question about the HTML you are converting:

Does it share a stylesheet with your web app?

If no — if it is a statement or a report, built for print, headings and paragraphs and one table, no custom typeface you would notice losing — a library is the right answer and you can stop reading after the next section.

If yes — if the invoice template is the same components, the same design tokens, the same flex and grid, the same web font as the page your users look at — then the only implementation that is guaranteed to agree with what you saw in the browser is the browser. Not because browsers are better software. Because the browser is the thing your CSS was written against. Every judgement you made while building that template — that the columns work, that the total lines up under its column — was a judgement about what that engine does.

Everything below follows from that one answer.

If a library is the right answer

Take WeasyPrint as the serious representative of this class. It is well built, actively maintained, and not frozen — releases still ship. For a large number of documents it is a better choice than anything else on this page.

In-process. No subprocess to supervise, no container to run, no browser to keep alive, no network hop. That is not a small win — it is one dependency instead of a piece of infrastructure. If your PDF is a document rather than an application screen, take it.

What it costs you is CSS coverage, and the project is honest about exactly where the edges are, which is worth more than a feature matrix. Its own documentation describes flexbox as working "for simple use cases but is not deeply tested", and grid as working "for simple cases, but has some limitations", with an explicit list of what is not supported — inline-grid, subgrids, repeat(auto-fill) among them. Elsewhere the project explains how it can be this small: "there is no user-interaction, no JavaScript, no live rendering (the document doesn't changed after it was first parsed) and no quirks mode".

Read that as a specification, not a warning. If your document needs none of those things, none of it is a cost. If your template is rendered by a JavaScript framework, or if you cannot enumerate the CSS features it uses because it inherits a design system, you are going to find the edges one property at a time, in production, on a Friday.

The other libraries in this class — xhtml2pdf, the Java PDF libraries, whatever your stack ships — trade differently, but the shape of the trade is the same: you write against a partial implementation of CSS and you discover which part by hitting it.

If you need the browser: running it yourself

Playwright or Puppeteer, and the render itself is genuinely ten lines. Correct output, because it is the engine your CSS was designed against.

The ten lines are not the work. The work is that you now operate a browser:

None of that is hard. All of it is yours now, including at 3am.

Pick this when you already run containers and adding one more is a normal Tuesday; when your volume is high enough that per-call pricing stops making sense; when you have a latency budget that cannot afford a network hop to someone else's region; or when the HTML you render is private enough that you would rather it never leave your network.

If you go this way, read the print-CSS half of why your HTML-to-PDF library mangles your invoices before you ship — backgrounds, page size, breaks inside table rows, and waiting for web fonts are the four things that bite everyone who drives Chromium directly, and they are all fixable in CSS and flags.

If you need the browser: renting it

Same engine, someone else's problem. You POST HTML, you get PDF bytes.

The trade is not free and you should count all three parts. It costs money every month, forever. There is a network hop your latency budget has to absorb. And your invoice pipeline now has a dependency you do not control — when it is down, you are down, and you will find out from a customer.

That last one is the whole thing. So the question to ask any vendor here is not which features they have. It is what they run on, what they measure, and what they admit to.

Full disclosure: I built one of these, so read this section as the ad and the rest of the page as the article.

shotpdf is Chromium behind an HTTP endpoint. One call, PDF bytes in the response, no job ID and no polling:

curl -X POST https://shotpdf.p.rapidapi.com/pdf \
  -H "Content-Type: application/json" \
  -H "X-RapidAPI-Key: YOUR_KEY" \
  -H "X-RapidAPI-Host: shotpdf.p.rapidapi.com" \
  -d '{"html":"<h1>Invoice #1042</h1><p>Total: $430.00</p>",
       "paper":"A4","margin":"10mm","print_background":true}' \
  --output invoice.pdf

There is a free tier, which is enough to put a real invoice through it rather than just confirm that it answers.

As for what it admits to: writing the previous article prompted me to test the web-font race on my own service, and it failed. Twenty renders of the same page with no font wait, three times over: the font landed in 14, 15 and 16 of them. The rest were a valid PDF with the wrong typeface and nothing in the response to tell you which one you got. It now waits for document.fonts.ready before printing, and the same test is 20 out of 20, including against the live service. Those pre-fix runs are from my own machine, so read them as evidence that the bug was real rather than as a production miss rate. The full account, including the caveats on that measurement, is in the other article.

And if you need an SLA, buy one. Some vendors sell this category with contractual uptime and support attached; this is not one of them, and no amount of published latency substitutes for a number someone is contractually on the hook for. If your finance team cannot email invoices when the renderer is down, that is a real requirement — go buy it.

If X then Y