Skip to content

wkhtmltopdf alternative: migrating to a modern renderer

, 6 min read

wkhtmltopdf turned HTML into PDF for more than a decade, and plenty of apps still shell out to it. The project is now archived on GitHub, its last release is 0.12.6 from June 2020, and its own status page says the WebKit inside it hasn’t been updated since 2012. This guide moves a wkhtmltopdf call to a current Chromium renderer, flag by flag.

Why move

  • Security. The project’s status page says: do not use wkhtmltopdf with any untrusted HTML, because it can lead to a complete takeover of the server it runs on.
  • CSS. An engine frozen in 2012 doesn’t know much of today’s CSS. Layouts that need workarounds there (floats instead of flexbox or grid, -webkit-box hacks) just work in Chromium.
  • No fixes are coming. An archived repository takes no issues or pull requests.

Before

Terminal (wkhtmltopdf)
wkhtmltopdf --page-size A4 -O Portrait \
  -T 20mm -B 20mm -L 16mm -R 16mm \
  --print-media-type \
  --footer-center "Page [page] of [topage]" --footer-font-size 8 \
  invoice.html invoice.pdf

After

Terminal
curl https://api.pagenull.dev/v1/pdf \
  -H "Authorization: Bearer $PAGENULL_API_KEY" \
  -H "Content-Type: application/json" \
  -o invoice.pdf \
  --data-binary @- <<EOF
{
  "html": $(jq -Rs . < invoice.html),
  "format": "A4",
  "margin": { "top": "20mm", "bottom": "20mm", "left": "16mm", "right": "16mm" },
  "pageNumbers": "Page {page} of {pages}",
  "pageNumbersPosition": "bottom-center"
}
EOF

jq -Rs . turns the HTML file into a JSON string. In application code you’d build the JSON with your language’s own library instead; see the guides for Python, PHP and Laravel and Node.js.

Flag by flag

wkhtmltopdfpagenull
--page-size A4format: "A4" (or width + height)
-O Landscapelandscape: true
-T -B -L -Rmargin.top, .bottom, .left, .right
--print-media-typeThe default here. For screen styles: emulateMedia: "screen"
--no-backgroundprintBackground: false
--zoom 0.9scale: 0.9
--header-html header.htmlheaderHtml: "<div>…</div>" (the markup, not a file)
--footer-center "[page] / [topage]"pageNumbers: "{page} / {pages}", pageNumbersPosition: "bottom-center"
--disable-javascriptjavascript: false
--javascript-delay 2000waitFor: "#ready": wait for an element instead of a fixed delay
--grayscaleNo equivalent. Use grayscale CSS in a print stylesheet.

CSS you can drop

  • page-break-inside: avoid on table rows and display: table-header-group on thead: the print reset sets both by default. See CSS for print.
  • Float- and table-based layouts kept for the old engine: flexbox and grid print correctly.
  • Fixed pixel widths that matched wkhtmltopdf’s default DPI: size the page with @page and lengths in mm.

Render a few real documents with "test": true (free, watermarked) and compare them page by page before switching. Expect differences in line breaks, because the font rendering is newer.

Sources

Facts about other projects and rules, checked on 4 November 2026.