wkhtmltopdf Is Deprecated: Best Alternatives for 2026

wkhtmltopdf is deprecated and unmaintained. Compare the best alternatives for HTML-to-PDF conversion in 2026, from headless Chrome to cloud APIs.

By LightningPDF Team · · 11 min read
wkhtmltopdf Is Deprecated: Best Alternatives for 2026
TL;DR: wkhtmltopdf was archived in 2023 with known security vulnerabilities. The best alternatives are headless Chrome (Puppeteer), Playwright, or cloud APIs like LightningPDF that handle infrastructure for you.

wkhtmltopdf Is Deprecated: Best Alternatives for 2026

If you are still using wkhtmltopdf in production, this is the article you need to read. The project was officially archived on GitHub in January 2023. The last release, version 0.12.6, shipped in June 2020. There have been no security patches, no bug fixes, and no maintainer activity for over three years. The Qt WebKit engine it depends on was itself abandoned years earlier.

wkhtmltopdf is not just unmaintained — it is actively dangerous to run in production. There are known, unpatched security vulnerabilities. The rendering engine cannot handle modern CSS. And every month that passes without updates widens the gap between what wkhtmltopdf can render and what the modern web actually looks like.

This guide explains why wkhtmltopdf became a risk, evaluates every viable alternative, and provides a concrete migration path to get your PDF generation onto a supported, secure foundation.

Why did wkhtmltopdf dominate for so long?

When wkhtmltopdf appeared in 2008, the alternatives for HTML-to-PDF conversion were grim. You could use Java-based libraries like iText or Flying Saucer (slow, limited CSS support, required a JVM). You could use PHP's DOMPDF or TCPDF (terrible rendering, minimal CSS support). Or you could pay thousands of dollars for PrinceXML.

wkhtmltopdf offered something none of these did: real browser rendering in a command-line tool. It embedded Qt WebKit, the same engine that powered early Safari and Chrome, and exposed it as a simple CLI. Run wkhtmltopdf input.html output.pdf and get a PDF that looked like what you saw in a browser. No Java runtime, no complex configuration, no license fees.

The advantages were significant:

  • Simple CLI interface. One command, two arguments. Any language could shell out to it.
  • Real browser rendering. CSS 2.1 support was excellent. Basic CSS3 worked. JavaScript executed.
  • Fast. 500ms to 1 second for most documents — faster than launching a full browser.
  • Free and open source. No license fees, no per-document costs.
  • Easy to install. Single binary or a package manager install on any Linux distro.
  • Mature ecosystem. Wrappers existed for every language: Python (pdfkit), Ruby (wicked_pdf), PHP (snappy/knp-snappy), Node.js (wkhtmltopdf), Go, and more.

For nearly a decade, wkhtmltopdf was the default answer to "how do I convert HTML to PDF?" Stack Overflow is still littered with answers recommending it. Package managers still list it. Tutorials from 2018 still rank on Google. The tool's ghost lives on long after its death.

Why wkhtmltopdf Is Now a Risk

The problems with running wkhtmltopdf in 2026 fall into three categories: security, rendering, and reliability.

Security Vulnerabilities

wkhtmltopdf processes untrusted HTML input and converts it to PDF. This is inherently a security-sensitive operation. The Qt WebKit engine it uses has known CVEs that will never be patched:

  • Server-Side Request Forgery (SSRF). wkhtmltopdf follows URLs in HTML content, including file:// protocol, localhost references, and internal network addresses. An attacker who controls the HTML input can read files from your server (file:///etc/passwd), probe your internal network, or access cloud metadata endpoints (http://169.254.169.254/).
  • Remote Code Execution. Qt WebKit has known memory corruption vulnerabilities in its JavaScript engine and HTML parser. These are patched in modern WebKit but will never be patched in wkhtmltopdf's frozen fork.
  • Denial of Service. Malformed HTML or infinite JavaScript loops can hang wkhtmltopdf processes indefinitely, consuming server resources.

If your wkhtmltopdf instance processes any user-supplied HTML — form data, email content, CMS text, API inputs — you have an exploitable attack surface.

Rendering Failures

wkhtmltopdf's Qt WebKit engine is frozen at approximately 2015 web standards. It does not support:

  • CSS Flexbox — The most common layout system on the modern web. Flex containers render as block elements with no flex behavior.
  • CSS Grid — Completely unsupported. Grid containers render as block elements with items stacked vertically.
  • CSS Custom Properties (variables)var(--color-primary) is not evaluated. Elements using CSS variables get no styling.
  • Modern selectors:is(), :where(), :has(), :not() with complex arguments all fail.
  • gap property — Even on non-flex/grid contexts, gap is unsupported.
  • calc() with mixed unitscalc(100% - 2rem) may not compute correctly.
  • Web fonts via @font-face — Inconsistent behavior, especially with WOFF2 format.
  • position: sticky — Falls back to position: static.

If your templates were written in 2018, they probably still render. If they were written or updated recently using modern CSS, they are likely broken in ways that are subtle and hard to debug.

Reliability Issues

Beyond security and rendering, there are practical reliability problems:

  • No package updates. Distros are dropping wkhtmltopdf from their repositories. Ubuntu 24.04 does not include it. You need to install from archived .deb files, which introduces dependency conflicts.
  • No bug fixes. Known bugs around header/footer rendering, table pagination, and memory leaks will never be fixed.
  • Dependency rot. The libraries wkhtmltopdf links against (OpenSSL, Qt, fontconfig) continue to update, and version mismatches cause crashes on newer operating systems.
  • Docker complexity. Running wkhtmltopdf in Docker requires pinning to older base images or manually installing deprecated library versions.

Alternative 1: Headless Chrome with Puppeteer

Puppeteer is a Node.js library that controls headless Chrome (or Chromium) for browser automation, including PDF generation. It is the most direct replacement for wkhtmltopdf because it provides real browser rendering — but with a modern, actively maintained engine.

How It Works

const puppeteer = require('puppeteer');

async function generatePDF(html) {
    const browser = await puppeteer.launch({ headless: true });
    const page = await browser.newPage();
    await page.setContent(html, { waitUntil: 'networkidle0' });
    const pdf = await page.pdf({
        format: 'A4',
        printBackground: true,
        margin: { top: '20mm', right: '15mm', bottom: '20mm', left: '15mm' }
    });
    await browser.close();
    return pdf;
}

Pros

  • Full CSS support (flexbox, grid, variables, everything Chrome supports)
  • JavaScript execution
  • Active development with regular updates
  • Large community and extensive documentation
  • Free and open source

Cons

  • Slow: 1 to 3 seconds per PDF (vs 500ms for wkhtmltopdf)
  • Memory-intensive: 100 to 300MB per Chrome instance
  • Requires infrastructure: Docker containers, process management, scaling logic
  • Cold start penalty: launching a new browser takes 3 to 5 seconds

Best For

Teams that need full browser rendering fidelity, already have Node.js infrastructure, and generate fewer than a few hundred PDFs per day. For a detailed cost analysis of running Puppeteer in production, see our LightningPDF vs Puppeteer comparison.

Alternative 2: Playwright

Playwright is Microsoft's answer to Puppeteer. It controls Chromium, Firefox, and WebKit browsers and offers a similar PDF generation API with some improvements.

How It Works

const { chromium } = require('playwright');

async function generatePDF(html) {
    const browser = await chromium.launch();
    const page = await browser.newPage();
    await page.setContent(html, { waitUntil: 'networkidle' });
    const pdf = await page.pdf({
        format: 'A4',
        printBackground: true,
        margin: { top: '20mm', right: '15mm', bottom: '20mm', left: '15mm' }
    });
    await browser.close();
    return pdf;
}

Pros

  • Same CSS support as Puppeteer (full modern CSS)
  • Multi-browser support (can render with WebKit for Safari-like output)
  • Better process management and auto-waiting
  • Strong TypeScript support
  • Active Microsoft backing

Cons

  • Same performance characteristics as Puppeteer (1 to 3 seconds, high memory)
  • Slightly larger install size (bundles three browser engines)
  • Same infrastructure requirements

Best For

Teams already using Playwright for testing who want to reuse their browser infrastructure for PDF generation. The multi-browser option is useful if you need to match a specific browser's rendering behavior.

Alternative 3: WeasyPrint

WeasyPrint is a Python library that converts HTML and CSS to PDF using its own rendering engine (not a browser). It targets the CSS Paged Media specification, which was designed specifically for print layouts.

How It Works

from weasyprint import HTML

def generate_pdf(html_string):
    return HTML(string=html_string).write_pdf()

# From a URL
pdf_bytes = HTML('https://example.com/report').write_pdf()

# From a file
pdf_bytes = HTML(filename='invoice.html').write_pdf()

Pros

  • Good CSS support (better than wkhtmltopdf, though not full Chrome-level)
  • CSS Paged Media support (running headers, footnotes, named pages)
  • Pure Python — no browser dependency
  • Lighter resource footprint than headless Chrome
  • Active development

Cons

  • Does not support JavaScript execution
  • Missing some modern CSS features (partial flexbox, no grid)
  • Slower than wkhtmltopdf for simple documents (1 to 2 seconds)
  • System dependencies required (Cairo, Pango, GDK-Pixbuf)
  • Font rendering differences from browsers

Best For

Python shops generating print-focused documents (books, academic papers, reports) where CSS Paged Media features matter more than pixel-perfect browser rendering.

Alternative 4: Cloud PDF APIs

Cloud APIs handle all the complexity — browser management, scaling, security, updates — behind a REST endpoint. You send HTML, you receive a PDF. No infrastructure to manage.

How It Works with LightningPDF

curl -X POST https://api.lightningpdf.dev/api/v1/pdf/generate \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"html": "<h1>Hello PDF</h1><p>Generated via API</p>", "options": {"format": "A4"}}' \
  -o output.pdf
import requests
import base64

response = requests.post(
    "https://api.lightningpdf.dev/api/v1/pdf/generate",
    headers={
        "X-API-Key": "your-api-key",
        "Content-Type": "application/json"
    },
    json={
        "html": "<h1>Hello PDF</h1><p>Generated via API</p>",
        "options": {
            "format": "A4",
            "print_background": True
        }
    }
)

result = response.json()
pdf_bytes = base64.b64decode(result["data"]["pdf"])
with open("output.pdf", "wb") as f:
    f.write(pdf_bytes)

Pros

  • Zero infrastructure management
  • Sub-100ms generation with native engine
  • Full CSS support (Chromium fallback for complex layouts)
  • Template marketplace and designer
  • Batch processing (up to 100 documents per call)
  • Automatic scaling
  • Regular updates and security patches you never think about

Cons

  • Recurring cost (though often cheaper than self-hosted infrastructure)
  • Data leaves your infrastructure (unless using the self-hosted plan)
  • External dependency (mitigated by high availability SLAs)

Best For

Any production application generating PDFs at any volume. The free tier covers 50 PDFs per month for testing and small projects. The Starter plan at $9/month covers 2,000 PDFs, and the Pro at $29/month covers 10,000. For a full comparison of API options, see our best PDF API guide for 2026.

Migration Guide: wkhtmltopdf to LightningPDF

If you are currently using wkhtmltopdf via a language wrapper, here is how to migrate. The core change is replacing the local binary invocation with an HTTP API call.

Python: pdfkit to LightningPDF

Before (pdfkit / wkhtmltopdf):

import pdfkit

# From HTML string
pdf = pdfkit.from_string(html_content, False, options={
    'page-size': 'A4',
    'margin-top': '20mm',
    'margin-right': '15mm',
    'margin-bottom': '20mm',
    'margin-left': '15mm',
})

with open('output.pdf', 'wb') as f:
    f.write(pdf)

After (LightningPDF API):

import requests
import base64

response = requests.post(
    "https://api.lightningpdf.dev/api/v1/pdf/generate",
    headers={
        "X-API-Key": "your-api-key",
        "Content-Type": "application/json"
    },
    json={
        "html": html_content,
        "options": {
            "format": "A4",
            "margin_top": "20mm",
            "margin_right": "15mm",
            "margin_bottom": "20mm",
            "margin_left": "15mm",
            "print_background": True
        }
    }
)

result = response.json()
pdf = base64.b64decode(result["data"]["pdf"])

with open("output.pdf", "wb") as f:
    f.write(pdf)

The interface is almost identical. The main differences: HTTP call instead of subprocess, API key authentication, and JSON options instead of CLI flags. For the full Python integration guide, see Generate PDFs in Python.

Node.js: wkhtmltopdf to LightningPDF

Before (node-wkhtmltopdf):

const wkhtmltopdf = require('wkhtmltopdf');
const fs = require('fs');

wkhtmltopdf(htmlContent, {
    pageSize: 'A4',
    marginTop: '20mm',
    marginBottom: '20mm',
}).pipe(fs.createWriteStream('output.pdf'));

After (LightningPDF API):

const fs = require('fs');

const response = await fetch('https://api.lightningpdf.dev/api/v1/pdf/generate', {
    method: 'POST',
    headers: {
        'X-API-Key': 'your-api-key',
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        html: htmlContent,
        options: {
            format: 'A4',
            margin_top: '20mm',
            margin_bottom: '20mm',
            print_background: true
        }
    })
});

const result = await response.json();
const pdfBuffer = Buffer.from(result.data.pdf, 'base64');
fs.writeFileSync('output.pdf', pdfBuffer);

See the full Node.js PDF generation tutorial for error handling, batch generation, and template usage.

Go: go-wkhtmltopdf to LightningPDF

Before (go-wkhtmltopdf):

pdfg, _ := wkhtmltopdf.NewPDFGenerator()
pdfg.AddPage(wkhtmltopdf.NewPageReader(strings.NewReader(htmlContent)))
pdfg.PageSize.Set(wkhtmltopdf.PageSizeA4)
pdfg.MarginTop.Set(20)
pdfg.Create()
pdf := pdfg.Bytes()

After (LightningPDF API):

payload := map[string]interface{}{
    "html": htmlContent,
    "options": map[string]interface{}{
        "format":           "A4",
        "margin_top":       "20mm",
        "print_background": true,
    },
}

body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST",
    "https://api.lightningpdf.dev/api/v1/pdf/generate",
    bytes.NewReader(body))
req.Header.Set("X-API-Key", "your-api-key")
req.Header.Set("Content-Type", "application/json")

resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()

var result struct {
    Data struct {
        PDF string `json:"pdf"`
    } `json:"data"`
}
json.NewDecoder(resp.Body).Decode(&result)
pdfBytes, _ := base64.StdEncoding.DecodeString(result.Data.PDF)
os.WriteFile("output.pdf", pdfBytes, 0644)

For the complete Go integration with error handling and batch support, see Generate PDFs in Go.

WordPress: Snappy/knp-snappy to LightningPDF Plugin

If your WordPress site uses the Snappy PHP wrapper for wkhtmltopdf, the cleanest migration is to the LightningPDF WordPress plugin. Install the plugin, enter your API key, and remove the Snappy dependency entirely. The plugin handles template rendering, caching, and WooCommerce integration out of the box. For a step-by-step walkthrough, see our WordPress post-to-PDF guide.

Comparison Table

Feature wkhtmltopdf Puppeteer Playwright WeasyPrint LightningPDF
Status Archived (2023) Active Active Active Active
Engine Qt WebKit (2015) Chromium Chromium/FF/WebKit Custom Native + Chromium
CSS Flexbox No Yes Yes Partial Yes
CSS Grid No Yes Yes No Yes
CSS Variables No Yes Yes Yes Yes
JavaScript Basic Full Full No Full (Chromium)
Speed 500ms-1s 1-3s 1-3s 1-2s <100ms (native)
Memory 50MB 100-300MB 100-300MB 80-150MB 0 (cloud)
Infrastructure Binary install Node + Chrome Node + browsers Python + Cairo API call
Security patches None (dead) Regular Regular Regular Managed
Cost Free Free* Free* Free Free tier, from $9/mo
Batch API No DIY DIY DIY Yes (100/call)
Templates No DIY DIY No Marketplace + designer

*Free software, but infrastructure costs $200-500/month in production. See our detailed cost analysis.

Migration Checklist

If you are planning to move off wkhtmltopdf, here is a practical checklist:

  1. Audit your templates. Identify every HTML template that wkhtmltopdf renders. Note which ones use modern CSS that wkhtmltopdf was already butchering.

  2. Choose your alternative. For most teams, a cloud API is the fastest path. If you need to keep rendering on-premises, Puppeteer or Playwright are the best options.

  3. Test rendering parity. Generate the same documents with both wkhtmltopdf and your new tool. Compare output side by side. Pay attention to page breaks, fonts, margins, and table rendering. Our page break guide covers the most common differences.

  4. Update your wrapper code. Replace the wkhtmltopdf library call with the new tool's API. The migration examples above show this is typically a 10 to 20 line change.

  5. Remove wkhtmltopdf. Uninstall the binary, remove it from your Docker images, delete it from your CI/CD pipelines. Do not leave it installed "just in case" — it is an unpatched attack surface.

  6. Monitor in production. Watch for rendering differences your tests missed. The first week after migration is when edge cases surface.

  7. Update your documentation. Remove any references to wkhtmltopdf from your internal docs, runbooks, and README files.

What About PrinceXML?

PrinceXML deserves mention as a high-end alternative. It has excellent CSS Paged Media support, produces beautiful print output, and is actively maintained. The catch is price: licenses start at $3,800 for a single server, with no free tier for production use.

If you generate professional publications (books, magazines, academic journals) and need advanced print features like footnotes, running headers, and cross-references, PrinceXML is worth evaluating. For everything else — invoices, reports, receipts, certificates, contracts — it is dramatically over-priced relative to alternatives. For a broader comparison, see our best PDF API guide for 2026.

Getting Started with LightningPDF

  1. Create a free account — 50 PDFs per month, no credit card.
  2. Replace your wkhtmltopdf call with the API (see migration examples above).
  3. Test your templates with the API documentation.
  4. Explore the template marketplace and designer for pre-built templates.
  5. For production volumes, upgrade to Starter ($9/month, 2,000 PDFs) or Pro ($29/month, 10,000 PDFs).

The migration from wkhtmltopdf to a modern alternative is not optional — it is a matter of when, not if. The longer you wait, the more security debt accumulates and the further your rendering drifts from what users expect. Every alternative on this list produces better output than wkhtmltopdf, and migration is simpler than you'd expect.

Frequently Asked Questions

Is wkhtmltopdf still safe to use in production?

No. wkhtmltopdf was archived in 2023 with known unpatched vulnerabilities including server-side request forgery and memory corruption in its Qt WebKit engine. If it processes any user-supplied HTML, it is an exploitable attack surface. Migrate to an actively maintained alternative immediately.

What is the easiest replacement for wkhtmltopdf?

A cloud PDF API like LightningPDF is the easiest drop-in replacement. The migration requires changing roughly 10 to 20 lines of code — replacing a subprocess call with an HTTP request. No browser binaries to install, no infrastructure changes, and the output quality is significantly better.

Can I migrate from wkhtmltopdf without changing my HTML templates?

In most cases, yes. Modern alternatives render the same HTML with better CSS support, so your existing templates will work and likely look better. The main adjustment is page break behavior, which differs slightly between rendering engines. Test your templates and adjust CSS page break rules as needed.

Additional Resources

L

LightningPDF Team

Building fast, reliable PDF generation tools for developers.

Ready to generate PDFs?

Start free with 50 PDFs per month. No credit card required.

Get Started Free