Migrating off wkhtmltopdf: a checklist that avoids the surprises
A practical sequence for replacing wkhtmltopdf in a production system: inventory, flag mapping, visual diff, cutover, rollback.
Updated 31 August 2026
wkhtmltopdf is archived and its WebKit is old, but it is also embedded in systems that have printed invoices correctly for years. Replacing it is a migration, not a swap. Here is the order that works.
1. Inventory the call sites
Grep for `wkhtmltopdf` and for the wrappers (`pdfkit`, `wicked_pdf`, `snappy`, `laravel-snappy`). List every flag in use. Most systems use six: page size, orientation, margins, print-media-type, header/footer, and zoom.
2. Map the flags
- `--page-size A4` → `format: "A4"`
- `--orientation Landscape` → `landscape: true`
- `--margin-*` → `margin` (one value or four)
- `--header-html file.html` → `header_html` inline; `[page]`/`[topage]` become `{{page}}`/`{{pages}}`
- `--print-media-type` → always on in a modern renderer
- `--zoom 0.8` → `scale: 0.8`
- `--disable-smart-shrinking` → not needed; Chromium does not shrink
See the full mapping table.
3. Expect the CSS to look better — and different
Old WebKit ignored most of flexbox and grid; your templates may contain workarounds (tables for layout, fixed pixel widths, `float` everywhere) that were compensating. In a modern engine those workarounds still render, but any CSS that was silently dropped now applies. Diff visually, page by page, on your five most common documents.
4. Fonts
wkhtmltopdf used the fonts installed on the server. A hosted renderer has its own. If your templates rely on a system font, ship it as a web font (see the fonts guide) or accept the metric-compatible fallback.
5. Run both in parallel
For a week, generate every document with both engines and store the new one alongside the old. Compare a sample daily. This costs almost nothing and catches the template with the odd `