Alternativen
wkhtmltopdf-Alternative: Was Sie jetzt nehmen, da es archiviert ist
wkhtmltopdf hat über zehn Jahre lang HTML in PDF verwandelt, und viele Rechnungs- und Berichtsstrecken laufen noch damit. Sein Repository ist seit Januar 2023 archiviert, seine Engine ist das WebKit aus Qt 4.8. Wir haben dieselben zwei HTML-Dateien mit wkhtmltopdf 0.12.6 und mit Headless Chrome gerendert: Zwölf von zwölf modernen CSS- und JavaScript-Funktionen scheiterten in wkhtmltopdf und funktionierten in Chrome.
Unten stehen die Ergebnisse, die Alternativen und eine Tabelle, die jede gängige wkhtmltopdf-Option einem API-Parameter zuordnet. Die Testdateien sind öffentlich, Sie können den Test also wiederholen.
| Status | Repository am 2. Januar 2023 archiviert; letzte Version 0.12.6 vom 10. Juni 2020 |
|---|---|
| Engine | WebKit aus Qt 4.8, laut Projekt seit 2012 nicht mehr aktualisiert |
| Sicherheit | CVE-2022-35583 (Server-Side Request Forgery, CVSS 9,8) betrifft 0.12.6; keine Version behebt sie |
| Unser Test | 12 von 12 modernen Funktionen scheiterten in wkhtmltopdf 0.12.6 und funktionierten in Chrome 156; zwei Gegenproben in älterer Schreibweise funktionierten in beiden |
| Vom Projekt empfohlen | WeasyPrint, Prince und Puppeteer als Alternativen |
| Umstieg auf eine API | Seitengröße, Ränder, Kopf- und Fußzeilen, Medientyp und Warten auf JavaScript haben je einen Parameter; siehe die Tabelle |
Wird wkhtmltopdf noch gepflegt?
Nein. Das Haupt-Repository wkhtmltopdf/wkhtmltopdf wurde am 2. Januar 2023 archiviert, das
Repository packaging, das die Programmdateien baut, folgte am 28. August 2023. Die letzte Version ist
0.12.6 vom 10. Juni 2020; die Version 0.12.7, die die
Statusseite des Projekts angekündigt hatte, ist
nie erschienen. Dieselbe Seite nennt den Grund: wkhtmltopdf baut auf QtWebKit auf, Qt 4 wird seit 2015 nicht
mehr unterstützt, und QtWebKit wurde 2016 aus Qt entfernt.
Zwei Sicherheitslücken sind wichtig, wenn Sie es noch einsetzen:
- CVE-2020-21365 (CVSS 7,5): Bis Version 0.12.5 konnte präpariertes HTML lokale Dateien
lesen. Deshalb sperrt 0.12.6 den Zugriff auf lokale Dateien standardmäßig, und deshalb steht in so vielen
Konfigurationen
--enable-local-file-access, das ihn wieder öffnet. - CVE-2022-35583 (CVSS 9,8): In 0.12.6 bringt ein
iframeim HTML den Server dazu, beliebige Adressen abzurufen, auch interne. Nach 0.12.6 gibt es keine Version mehr, also auch keine Korrektur.
Die Statusseite sagt es deutlich: wkhtmltopdf nie mit nicht vertrauenswürdigem HTML verwenden. Für neue Projekte empfiehlt sie WeasyPrint, Prince oder Puppeteer.
Der Test: dasselbe HTML, zwei Engines
Wir haben zwei in sich geschlossene HTML-Dateien geschrieben, mit eingebetteter Schrift, damit keine der beiden Engines etwas nachladen musste:
- Ein Funktionstest mit zwölf CSS- und JavaScript-Funktionen, die Vorlagen heute nutzen, dazu zwei Gegenproben in der Schreibweise, die ältere Engines erwarten. Wo ein Test sich selbst prüfen kann, wird er grün, wenn die Funktion klappt, und bleibt rot, wenn nicht.
- Eine zweiseitige Rechnung, geschrieben wie eine heutige Vorlage: Kopfzeile mit Flexbox, Adressblock mit Grid, CSS-Variablen für die Markenfarbe, eine WOFF2-Webschrift, eine Tabelle über einen Seitenumbruch und ein Fälligkeitsdatum, das ein kurzes Skript einsetzt.
Beide Dateien liefen durch wkhtmltopdf 0.12.6 (with patched Qt), die offizielle macOS-Fassung,
mit Standardeinstellungen bis auf A4 und 15 mm Rand, und durch Chrome 156.0.8078.76, headless,
mit page.pdf() von Playwright, A4, denselben Rändern und mit Hintergründen. Die Engine unserer API
(2026.4) läuft mit Chrome 153; jede Funktion im Test ist viel älter als beide Versionen.
| Test | wkhtmltopdf 0.12.6 | Chrome 156 |
|---|---|---|
1. Flexboxdisplay: flex | Gescheitert: Kästen untereinander | Funktioniert |
| 2. gap in einer Flex-Zeile | Gescheitert: Kästen untereinander, kein Abstand | Funktioniert |
3. CSS Gridgrid-template-columns | Gescheitert: sechs Kästen in einer Spalte | Funktioniert |
4. CSS-Variablenvar(--brand) | Gescheitert: Ersatzfarbe | Funktioniert |
| 5. WOFF2-Webschrift | Gescheitert: Georgia statt Inter | Funktioniert |
6. calc() | Gescheitert | Funktioniert |
7. clamp() | Gescheitert | Funktioniert |
8. rgb(31 157 85 / 100%) | Gescheitert | Funktioniert |
9. Selektor :is() | Gescheitert | Funktioniert |
10. aspect-ratio | Gescheitert | Funktioniert |
11. JavaScript ab 2015const, Pfeilfunktionen, Template-Literale | Gescheitert: Skript lief nicht | Funktioniert |
12. Intl.NumberFormat | Gescheitert: Intl gibt es nicht | Funktioniert |
| 13. Gegenprobe: JavaScript in ES5-Schreibweise | Funktioniert | Funktioniert |
14. Gegenprobe: display: -webkit-box | Funktioniert | Funktioniert |
Die Gegenproben sind wichtig: wkhtmltopdf ist nicht kaputt für das HTML, für das es gebaut wurde. Eine Vorlage,
die vor Jahren mit -webkit-box, Floats und ES5-Skripten geschrieben wurde, rendert weiter. Was
scheitert, ist CSS und JavaScript aus dem letzten Jahrzehnt, und genau das liefern Designer, Frameworks und
Code-Generatoren heute.
Was in einer echten Vorlage kaputtgeht
- Das Layout fällt zusammen. Logo und Rechnungstitel stehen nicht mehr in einer Zeile; Absender, Empfänger und die vier Rechnungsangaben stehen untereinander statt nebeneinander.
- Jede Farbe aus einer Variablen fehlt. Die Markenlinie unter dem Kopf, der farbige Titel und der Hintergrund des Tabellenkopfs verschwinden. Die Schrift im Tabellenkopf ist weiß, die Spaltentitel werden also unsichtbar.
- Die Schrift wechselt. Die WOFF2-Datei wird ignoriert, die ganze Rechnung erscheint in Georgia.
- Was das Skript einsetzt, fehlt. Das Fälligkeitsdatum bleibt leer, weil das Skript
constverwendet, das die Engine nicht versteht. - Alles ist etwas kleiner. Das standardmäßige „Smart Shrinking“ von wkhtmltopdf skaliert die Seite; wir haben die Standardeinstellung gelassen, wie die meisten Installationen.
Chrome hat die Rechnung wie entworfen gerendert und den Tabellenkopf oben auf Seite 2 wiederholt.
Die Alternativen im Vergleich
Welcher Ersatz passt, hängt davon ab, ob Ihr HTML JavaScript braucht und wo die PDFs entstehen dürfen.
| Option | Engine | JavaScript | Läuft | Passt, wenn |
|---|---|---|---|---|
| WeasyPrint | Eigene Layout-Engine (Python), stark bei Druck-CSS; BSD-Lizenz | Nein | Auf Ihren Servern | Berichte aus eigenem HTML ohne Skripte |
| Prince | Eigene Engine, stark bei Druck-CSS; kommerzielle Lizenz | Eingeschränkt | Auf Ihren Servern | Drucklayout auf Verlagsniveau, mit Budget für Lizenzen |
| Puppeteer oder Playwright | Chrome; Apache-2.0-Lizenz | Ja | Auf Ihren Servern | Sie betreiben schon Browser, oder Dokumente dürfen Ihr Netz nicht verlassen |
| Gotenberg | Chrome (und LibreOffice) hinter einer HTTP-API in Docker; MIT-Lizenz | Ja | Auf Ihren Servern | Sie wollen eine API, müssen sie aber selbst hosten |
| Gehostete HTML-to-PDF-API, z. B. Dynamic Document API | Chrome, vom Anbieter aktuell gehalten (bei uns: Engine 2026.4, Chrome 153) | Ja, mit Wartebedingungen | Beim Anbieter (bei uns: EU, Server in Deutschland) | Sie wollen modernes Ergebnis, ohne einen Browser zu betreiben oder zu patchen |
Prince und WeasyPrint setzen manche Druckdetails besser als Browser, etwa Fußnoten oder laufende Kopfzeilen aus CSS, aber keiner von beiden hat eine moderne JavaScript-Engine. Wenn Ihre Vorlagen Diagramme oder Formatierung im Browser nutzen, ist eine Option mit Chrome der sicherere Umstieg. Selbst betriebenes Chrome heißt: Sie betreiben den Browser, mit Updates alle paar Wochen, Schriften, Speicher und hängenden Prozessen. Eine gehostete API verlagert diese Arbeit zum Anbieter. Der Free-Tarif von Dynamic Document API enthält 50 Renderings im Monat ohne Kreditkarte; Bezahltarife beginnen bei 15 € im Monat bei jährlicher Zahlung. In unserem Renderzeit-Benchmark brauchte eine einseitige Rechnung im Median 124 ms im Renderer.
Von wkhtmltopdf-Optionen zu API-Parametern
Die meisten wkhtmltopdf-Aufrufe lassen sich eins zu eins übertragen. Links steht die wkhtmltopdf-Option, rechts
das Feld im pdf-Objekt von Dynamic Document API oder das genannte Feld der Anfrage.
| wkhtmltopdf | Dynamic Document API |
|---|---|
--page-size A4, -O Landscape | paper.size: "A4", orientation: "landscape" |
--page-width, --page-height | paper.width, paper.height (CSS-Längen wie "80mm") |
-T, -B, -L, -R | margin.top, margin.bottom, margin.left, margin.right |
--header-center "Seite [page] von [topage]" | header.center: "Seite {{page}} von {{pages}}"; ebenso left und 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; Elemente mit den Klassen page und pages bekommen die Seitenzahlen |
| Standard (Bildschirm-Medium) | emulate_media: "screen" |
--print-media-type | Der Standard (emulate_media: "print") |
--no-background | print_background: false |
--disable-javascript | javascript: false |
--javascript-delay 2000 | wait.until: "delay", wait.delay_ms: 2000; besser: "networkidle" oder "selector" |
--window-status ready | wait.until: "ready_flag", und Ihr Skript setzt window.__DYNAMIC_DOCUMENT_API_READY__ = true |
--zoom 1.2 | scale: 1.2 (0,1 bis 2,0) |
--outline | outline: true (Lesezeichen aus h1 bis h6) |
--title | metadata.title |
--cookie, --custom-header, --username/--password | Für eine URL: http.cookies, http.headers, http.basic_auth |
--enable-local-file-access | Nicht nötig: CSS und Bilder ins HTML, oder per HTTPS-URL verlinken |
--disable-smart-shrinking | Nicht nötig: Chrome verkleinert nicht; nutzen Sie scale, wenn Sie sich darauf verlassen haben |
Drei Dinge haben kein direktes Gegenstück: die erzeugte Inhaltsverzeichnis-Seite
(toc; nutzen Sie outline für PDF-Lesezeichen oder bauen Sie die Liste im HTML), die
Platzhalter [section] und [subsection], und --dpi, das Chrome für
Vektorausgabe nicht braucht. Alle Optionen: PDF-Optionen und
Kopf- und Fußzeilen (Englisch).
Vorher und nachher: der Code
Auf der Kommandozeile
Vorher, mit wkhtmltopdf:
wkhtmltopdf --page-size A4 -T 20mm -B 20mm --print-media-type \
--footer-center "Seite [page] von [topage]" \
rechnung.html rechnung.pdf
Nachher, mit Dynamic Document API. jq verpackt die HTML-Datei in die JSON-Anfrage; das
Druck-Medium ist Standard und braucht keine Option:
jq -n --rawfile html rechnung.html '{
html: $html,
pdf: {
paper: { size: "A4" },
margin: { top: "20mm", bottom: "20mm" },
footer: { enabled: true, center: "Seite {{page}} von {{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 rechnung.pdf
Aus Python (pdfkit)
Vorher:
import pdfkit
pdfkit.from_file("rechnung.html", "rechnung.pdf",
options={"page-size": "A4", "margin-top": "20mm", "margin-bottom": "20mm"})
Nachher, mit requests:
import os
import requests
with open("rechnung.html", encoding="utf-8") as f:
html = f.read()
res = requests.post(
"https://api.dynamicdocumentapi.com/v1/pdf/from-html",
headers={"Authorization": f"Bearer {os.environ['DYNAMIC_DOCUMENT_API_KEY']}"},
json={
"html": html,
"pdf": {"paper": {"size": "A4"}, "margin": {"top": "20mm", "bottom": "20mm"}},
"delivery": "binary",
},
timeout=60,
)
res.raise_for_status()
with open("rechnung.pdf", "wb") as f:
f.write(res.content)
Ein Unterschied, den Sie einplanen sollten: wkhtmltopdf liest Bilder und Stylesheets von Ihrer Festplatte, eine API kann das nicht. Schreiben Sie das CSS ins HTML, betten Sie kleine Bilder als Data-URI ein und verlinken Sie größere per HTTPS-URL. Wenn Sie dasselbe Layout immer wieder rendern, speichern Sie es einmal als Vorlage und schicken nur noch die Daten.
pdfkit, Snappy, wicked_pdf und andere Wrapper
Viele Projekte rufen wkhtmltopdf nie direkt auf. Sie nutzen eine Bibliothek, die das tut:
- Python: pdfkit
- Ruby: PDFKit und wicked_pdf
- PHP: Snappy von KnpLabs und laravel-snappy
- .NET: DinkToPdf und Rotativa
- Node.js: das Paket
wkhtmltopdf
Einige dieser Bibliotheken werden noch gepflegt, aber alle betreiben darunter dieselbe archivierte Engine und teilen damit jedes Ergebnis aus dem Test oben. Der Wechsel von einem Wrapper zum anderen ändert nichts. Tauschen Sie den Renderer aus: Die Anfrage aus dem vorigen Abschnitt funktioniert aus jeder Sprache, die eine HTTPS-Anfrage senden kann, und unsere SDKs und Integrationen (Englisch) nehmen Ihnen das ab.
Wenn Sie wkhtmltopdf vorerst behalten müssen
Ein Umstieg braucht Zeit. Bis er fertig ist, begrenzen Sie das Risiko:
- Rendern Sie nur HTML, das Sie kontrollieren. Geben Sie wkhtmltopdf nie HTML von Nutzern, in HTML umgewandeltes Markdown oder fremde URLs.
- Lassen Sie den Dateizugriff aus. Wenn Sie
--enable-local-file-accessbrauchen, erlauben Sie mit--allownur ein einziges Verzeichnis. - Schotten Sie den Prozess ab. Lassen Sie ihn in einem Container ohne Zugang zu Ihrem internen Netz laufen, damit die Request Forgery aus CVE-2022-35583 nichts erreicht. Die Statusseite des Projekts schlägt zusätzlich AppArmor oder SELinux vor.
- Schreiben Sie für die alte Engine. Nutzen Sie
display: -webkit-boxoder Floats statt Flexbox und Grid, feste Farben statt Variablen, ES5-Skripte und auf dem Rechner installierte Schriften.
Häufige Fragen
Wird wkhtmltopdf noch gepflegt?
Nein. Das Repository von wkhtmltopdf auf GitHub ist seit dem 2. Januar 2023 archiviert, die letzte Version ist 0.12.6 vom 10. Juni 2020. Weitere Korrekturen kommen nicht mehr, auch keine Sicherheitsupdates. Die Statusseite des Projekts nennt WeasyPrint, Prince und Puppeteer als Alternativen.
Was ist die beste Alternative zu wkhtmltopdf?
Das hängt von Ihrem HTML ab. Für Berichte ohne JavaScript beherrschen WeasyPrint (Open Source) und Prince (kommerziell) Druck-CSS gut. Für modernes CSS und JavaScript auf eigenen Servern eignet sich Headless Chrome über Puppeteer oder Playwright oder Gotenberg in Docker. Wer gar keinen Browser betreiben will, nimmt eine gehostete HTML-to-PDF-API wie Dynamic Document API mit aktuellem Chromium; der Free-Tarif enthält 50 Renderings im Monat.
Unterstützt wkhtmltopdf Flexbox und CSS Grid?
Nicht in der heutigen Schreibweise. In unserem Test mit wkhtmltopdf 0.12.6 wurden display: flex, gap und display: grid ignoriert, die Kästen standen untereinander. Nur die Flexbox-Schreibweise von 2009, display: -webkit-box, funktionierte.
Ist wkhtmltopdf sicher?
Nur mit HTML, das Sie selbst kontrollieren, und abgeschottet. CVE-2022-35583, eine Server-Side Request Forgery über ein iframe, von der NVD mit 9,8 (kritisch) bewertet, betrifft Version 0.12.6, und keine Version behebt sie. Die Statusseite des Projekts warnt davor, wkhtmltopdf mit nicht vertrauenswürdigem HTML zu verwenden.
Warum hilft der Wechsel von pdfkit zu einem anderen wkhtmltopdf-Wrapper nicht?
pdfkit (Python und Ruby), Snappy und laravel-snappy (PHP), wicked_pdf (Ruby), DinkToPdf und Rotativa (.NET) rufen alle das Programm oder die Bibliothek von wkhtmltopdf auf. Sie teilen seine Engine und damit seine Grenzen. Für modernes CSS und JavaScript müssen Sie den Renderer austauschen, nicht den Wrapper.
Wie übertrage ich Kopf- und Fußzeilen aus wkhtmltopdf?
In Dynamic Document API werden --header-center und --footer-center zu pdf.header.center und pdf.footer.center, mit {{page}} statt [page] und {{pages}} statt [topage]. --header-html und --footer-html werden zu pdf.header.html und pdf.footer.html; Elemente mit den Klassen page und pages bekommen die Seitenzahlen.
Testdateien und Zitat
- Testdateien, Skript und Ergebnisse (ZIP): beide HTML-Dateien, das Chrome-Skript, die Befehle und die vier PDFs
- Funktionstest als PDF: wkhtmltopdf, Chrome
- Rechnung als PDF: wkhtmltopdf, Chrome
- Diese Seite als Markdown (Englisch), für Sprachmodelle und eigene Notizen
Testdateien und Ergebnisse stehen unter CC BY 4.0: Verwenden Sie sie in eigenen Artikeln, Umstiegsanleitungen und Präsentationen, solange Sie Dynamic Document API nennen und auf diese Seite verlinken. Ein Vorschlag zum Zitieren:
Dynamic Document API (2026). wkhtmltopdf und Chrome im Vergleich: HTML-zu-PDF-Test, November 2026. https://dynamicdocumentapi.com/de/alternativen/wkhtmltopdf/
Einen Fehler gefunden, oder eine Funktion, die wir testen sollen? Schreiben Sie an support@dynamicdocumentapi.com.
Ersetzen Sie wkhtmltopdf durch eine API-Anfrage
Aktuelles Chromium, nichts zu installieren oder zu patchen. 50 Renderings im Monat im Free-Tarif, ohne Kreditkarte.
Kostenlos starten