# wkhtmltopdf alternative: what to use now that it is archived (Dynamic Document API, November 2026)

Source: https://dynamicdocumentapi.com/alternatives/wkhtmltopdf/ · Published by Dynamic Document API on 1 November 2026 ·
Test files and results licensed under CC BY 4.0 (name Dynamic Document API and link to the source).

We rendered the same two HTML files with wkhtmltopdf 0.12.6 and with headless Chrome. Twelve of twelve modern CSS and
JavaScript features failed in wkhtmltopdf and worked in Chrome; two controls in older syntax worked in both.

## At a glance

- Status: the repository `wkhtmltopdf/wkhtmltopdf` was archived on 2 January 2023, the `packaging` repository on
  28 August 2023. The last release is 0.12.6 from 10 June 2020; the announced 0.12.7 never appeared.
- Engine: the WebKit of Qt 4.8. According to the project's status page (https://wkhtmltopdf.org/status.html), Qt 4 has
  not been supported since 2015, its WebKit has not been updated since 2012, and QtWebKit was removed from Qt in 2016.
- Security: CVE-2020-21365 (CVSS 7.5) let crafted HTML read local files up to 0.12.5, which is why 0.12.6 blocks local
  file access by default. CVE-2022-35583 (CVSS 9.8, server-side request forgery through an iframe) affects 0.12.6 and
  has no fix. The status page warns against using wkhtmltopdf with any untrusted HTML.
- Alternatives named by the project: WeasyPrint, Prince and Puppeteer.

## The test

Two self-contained HTML files (font embedded, nothing loaded from the network or the disk):

- A feature test: twelve CSS and JavaScript features plus two controls. Self-checking tests turn green when the feature
  works and stay red when it does not.
- A two-page invoice: flexbox header, grid address block, CSS variables, a WOFF2 web font, a table across a page break,
  a due date set by a script.

Engines: wkhtmltopdf 0.12.6 (with patched Qt), official macOS build, default settings apart from A4 and 15 mm margins;
Chrome 156.0.8078.76, headless, Playwright's `page.pdf()`, A4, the same margins, backgrounds on. Tested on
1 November 2026. Dynamic Document API's engine 2026.4 runs Chrome 153.

| Test | wkhtmltopdf 0.12.6 | Chrome 156 |
|---|---|---|
| 1. Flexbox (`display: flex`) | Failed: boxes stacked | Worked |
| 2. `gap` in a flex row | Failed: boxes stacked, no gap | Worked |
| 3. CSS grid (`grid-template-columns`) | Failed: one column of six boxes | Worked |
| 4. CSS variables (`var(--brand)`) | Failed: fallback colour | Worked |
| 5. WOFF2 web font | Failed: fell back to Georgia | Worked |
| 6. `calc()` | Failed | Worked |
| 7. `clamp()` | Failed | Worked |
| 8. `rgb(31 157 85 / 100%)` | Failed | Worked |
| 9. `:is()` selector | Failed | Worked |
| 10. `aspect-ratio` | Failed | Worked |
| 11. JavaScript from 2015 on (`const`, arrow functions, template literals) | Failed: the script did not run | Worked |
| 12. `Intl.NumberFormat` | Failed: `Intl` does not exist | Worked |
| 13. Control: JavaScript in ES5 syntax | Worked | Worked |
| 14. Control: `display: -webkit-box` | Worked | Worked |

In the invoice, wkhtmltopdf stacked the header and the address block, dropped every colour set through a CSS variable
(the white table header text became invisible), replaced the WOFF2 font with Georgia, left the script-set due date empty
and scaled the page down through its default "smart shrinking". Chrome rendered the invoice as designed and repeated the
table header on page 2.

## The alternatives

| Option | Engine | JavaScript | Runs | Good fit when |
|---|---|---|---|---|
| WeasyPrint | Its own layout engine (Python), strong on print CSS; BSD licence | No | On your servers | Reports from HTML you control, without scripts |
| Prince | Its own engine, strong on print CSS; commercial licence | Limited | On your servers | Publishing-grade print layout with a budget for licences |
| Puppeteer or Playwright | Chrome; Apache 2.0 licence | Yes | On your servers | You already run browsers, or documents must not leave your network |
| Gotenberg | Chrome (and LibreOffice) behind an HTTP API in Docker; MIT licence | Yes | On your servers | You want an API but must host it yourself |
| Hosted HTML-to-PDF API, e.g. Dynamic Document API | Chrome, kept current by the provider (engine 2026.4, Chrome 153) | Yes, with wait conditions | At the provider (EU, servers in Germany) | You want modern output without running or patching a browser |

Dynamic Document API: free plan with 50 renders a month, no credit card; paid plans from €15 a month, billed annually.

## From wkhtmltopdf options to Dynamic Document API parameters (`pdf` object)

| wkhtmltopdf | Dynamic Document API |
|---|---|
| `--page-size A4`, `-O Landscape` | `paper.size: "A4"`, `orientation: "landscape"` |
| `--page-width`, `--page-height` | `paper.width`, `paper.height` (CSS lengths) |
| `-T`, `-B`, `-L`, `-R` | `margin.top`, `margin.bottom`, `margin.left`, `margin.right` |
| `--header-center "Page [page] of [topage]"` | `header.center: "Page {{page}} of {{pages}}"`; also `left`, `right` |
| `--footer-center`, `--footer-left`, `--footer-right` | `footer.center`, `footer.left`, `footer.right` |
| `[date]`, `[title]` | `{{date}}`, `{{title}}` |
| `--header-html`, `--footer-html` | `header.html`, `footer.html`; elements with the classes `page` and `pages` receive the numbers |
| Default (screen media) | `emulate_media: "screen"` |
| `--print-media-type` | The default (`emulate_media: "print"`) |
| `--no-background` | `print_background: false` |
| `--disable-javascript` | `javascript: false` |
| `--javascript-delay 2000` | `wait.until: "delay"`, `wait.delay_ms: 2000`; better `"networkidle"` or `"selector"` |
| `--window-status ready` | `wait.until: "ready_flag"`, the page sets `window.__DYNAMIC_DOCUMENT_API_READY__ = true` |
| `--zoom 1.2` | `scale: 1.2` (0.1 to 2.0) |
| `--outline` | `outline: true` |
| `--title` | `metadata.title` |
| `--cookie`, `--custom-header`, `--username`/`--password` | For a URL: `http.cookies`, `http.headers`, `http.basic_auth` |
| `--enable-local-file-access` | Not needed: put CSS and images in the HTML or link them by HTTPS URL |
| `--disable-smart-shrinking` | Not needed: Chrome does not shrink; use `scale` |

No direct counterpart: the `toc` page (use `outline` for bookmarks), the `[section]` and `[subsection]` placeholders,
and `--dpi`.

## Before and after

```bash
# Before
wkhtmltopdf --page-size A4 -T 20mm -B 20mm --print-media-type \
  --footer-center "Page [page] of [topage]" invoice.html invoice.pdf

# After
jq -n --rawfile html invoice.html '{
  html: $html,
  pdf: {
    paper:  { size: "A4" },
    margin: { top: "20mm", bottom: "20mm" },
    footer: { enabled: true, center: "Page {{page}} of {{pages}}" }
  },
  delivery: "binary"
}' | curl https://api.dynamicdocumentapi.com/v1/pdf/from-html \
  -H "Authorization: Bearer $DYNAMIC_DOCUMENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d @- --output invoice.pdf
```

wkhtmltopdf reads images and stylesheets from disk; an API cannot. Inline the CSS, embed small images as data URIs and
link larger ones by HTTPS URL.

## Wrapper libraries

pdfkit (Python), PDFKit and wicked_pdf (Ruby), KnpLabs Snappy and laravel-snappy (PHP), DinkToPdf and Rotativa (.NET)
and the Node.js package `wkhtmltopdf` all run the wkhtmltopdf binary or library, so they share every result above.
Switching wrappers changes nothing; replace the renderer.

## If you have to keep wkhtmltopdf for now

- Render only HTML you control; never user-supplied HTML or third-party URLs.
- Keep local file access off; if you need `--enable-local-file-access`, allow one directory with `--allow`.
- Run it in a container without access to your internal network; the status page also suggests AppArmor or SELinux.
- Write for the old engine: `display: -webkit-box` or floats, plain colours, ES5 scripts, fonts installed on the machine.

## Downloads

- Test files, Chrome script, commands and the four PDFs (ZIP): https://dynamicdocumentapi.com/assets/alternatives/wkhtmltopdf-test-2026-11.zip
- Feature test: https://dynamicdocumentapi.com/assets/alternatives/wkhtmltopdf-css-feature-test.pdf and
  https://dynamicdocumentapi.com/assets/alternatives/chrome-css-feature-test.pdf
- Invoice: https://dynamicdocumentapi.com/assets/alternatives/wkhtmltopdf-invoice.pdf and
  https://dynamicdocumentapi.com/assets/alternatives/chrome-invoice.pdf

Suggested citation: Dynamic Document API (2026). wkhtmltopdf vs. Chrome: HTML-to-PDF test, November 2026.
https://dynamicdocumentapi.com/alternatives/wkhtmltopdf/
