wkhtmltopdf is discontinued. The GitHub repository is archived, the last upstream release dates from 2020 (the last packaged build from May 2023), and Ubuntu 26.04 and Debian 13 no longer package it at all. Plenty of invoice jobs, reporting scripts and ERP installs still call it, though, so this guide gets the patched build running on current Ubuntu and Debian releases and shows what to move to next.
To install wkhtmltopdf on Ubuntu or Debian today, you need the right package from the archived wkhtmltopdf packaging releases, because the distro builds are either gone or unpatched. The steps below fix the “has no installation candidate” error and the 404 that older guides hit. They also cover local file access, page numbers, wkhtmltoimage and pdfkit. Every command was run on Ubuntu 26.04, Ubuntu 24.04, Debian 13 and Debian 12 in October 2026.
wkhtmltopdf is archived: what that means for you
The binary still works. Nothing else about it will change. The main repository is archived, and the packaging repository that builds the patched-Qt binaries is archived too. The maintainer’s status page is blunt about it: the WebKit engine has not been updated since 2012, and untrusted HTML can lead to a full takeover of the server running it.
That warning is not theoretical. CVE-2022-35583 is a CVSS 9.8 SSRF in 0.12.6, and no fix will come. Run it only on HTML your own application generates. For anything new, read our benchmark of wkhtmltopdf alternatives first. It tests eleven replacements on the same invoice and report documents.
Choose the right wkhtmltopdf package
Two builds exist and they behave differently. The distro package links against the stock Qt WebKit and ignores headers, footers, the table of contents and several page options. The patched build from the packaging repo bundles its own patched Qt and supports all of them. Each release is in a different state:
| Release | Distro package (apt) | Patched build to install |
|---|---|---|
| Ubuntu 26.04 LTS | None, apt reports Candidate: (none) | jammy .deb |
| Ubuntu 24.04 LTS | 0.12.6-2build2, unpatched Qt | jammy .deb |
| Debian 13 (trixie) | None, apt reports Candidate: (none) | bookworm .deb |
| Debian 12 (bookworm) | 0.12.6-2+b1, unpatched Qt | bookworm .deb |
No builds exist for the noble (24.04), resolute (26.04) or trixie (13) codenames. The jammy package runs on both current Ubuntu LTS releases, and the bookworm package runs on both Debian releases. That is the whole trick.
The unpatched distro build announces its limits on the first run. On Ubuntu 24.04, a footer flag produced this warning and a PDF without any footer:
The switch --footer-center, is not support using unpatched qt, and will be ignored.QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-josphat'
Debian 12’s package printed the same line. If which wkhtmltopdf returns /usr/bin/wkhtmltopdf, you have the distro build. Remove it before you continue:
sudo apt purge -y wkhtmltopdf
Install the patched wkhtmltopdf build
Start with the tools the install needs:
sudo apt update
sudo apt install -y curl poppler-utils
The release tag, the package suite and the CPU architecture all go into the download URL. Detect all three instead of typing them. The suite is jammy on Ubuntu and its derivatives and bookworm on Debian. The fallback tag only applies if the GitHub API rate limit returns nothing, and the archived repo will never publish a newer one:
. /etc/os-release
case " $ID $ID_LIKE " in
*" ubuntu "*) WK_SUITE=jammy ;;
*" debian "*) WK_SUITE=bookworm ;;
*) echo "Unsupported distribution: $ID" ;;
esac
ARCH=$(dpkg --print-architecture)
WK_TAG=$(curl -s https://api.github.com/repos/wkhtmltopdf/packaging/releases/latest | grep -oP '"tag_name": "\K[^"]+')
WK_TAG=${WK_TAG:-0.12.6.1-3} # pin: archived repo, last tag https://github.com/wkhtmltopdf/packaging/releases
echo "$WK_TAG $WK_SUITE $ARCH"
If the first line prints Unsupported distribution, stop here: the packaging repo only builds for Debian and Ubuntu families. On Ubuntu 26.04 the three values came back as:
0.12.6.1-3 jammy amd64
Debian 12 and 13 print bookworm in the middle. Download the matching package and install it with apt, which pulls the font and X11 libraries it depends on:
curl -fsSLO "https://github.com/wkhtmltopdf/packaging/releases/download/${WK_TAG}/wkhtmltox_${WK_TAG}.${WK_SUITE}_${ARCH}.deb"
sudo apt install -y "./wkhtmltox_${WK_TAG}.${WK_SUITE}_${ARCH}.deb"
The package lands as wkhtmltox, and both binaries go to /usr/local/bin. Confirm the patched build is the one on your PATH:
wkhtmltopdf --version
which wkhtmltopdf wkhtmltoimage
The version string must say “with patched qt”:
wkhtmltopdf 0.12.6.1 (with patched qt)
/usr/local/bin/wkhtmltopdf
/usr/local/bin/wkhtmltoimage
The same output appeared on all four releases. The packaging repo also publishes arm64 builds for jammy and bookworm, and the ARCH variable selects them on ARM hosts.
Error: E: Package ‘wkhtmltopdf’ has no installation candidate
This is what sudo apt install wkhtmltopdf prints on Debian 13 and Ubuntu 26.04. Debian removed the package from testing in February 2025, before trixie shipped, and Ubuntu dropped it in 25.04, so 26.04 is the first LTS without it. apt knows the name only because other packages still mention it. Check it yourself:
apt-cache policy wkhtmltopdf
An empty candidate confirms there is nothing to install from the archive:
wkhtmltopdf:
Installed: (none)
Candidate: (none)
Version table:
The fix is the patched .deb from the install section above. There is no repository to add.
Error: curl: (22) The requested URL returned error: 404
Many guides, including an older version of this one, build the download URL from lsb_release -cs. Of the four releases tested here, that works on Debian 12 only. Ubuntu 26.04 resolves to resolute, Ubuntu 24.04 to noble and Debian 13 to trixie, and the packaging repo never published those files:
curl: (22) The requested URL returned error: 404
Map the release to jammy or bookworm as shown above and the download succeeds. If you searched for the “jammy deb on Ubuntu 24.04” question, the answer is yes: it installs and passes every test in this guide.
Convert an HTML file to PDF
Create a working directory with a small invoice that references a local logo, which is where most real templates trip:
mkdir -p ~/invoices && cd ~/invoices
vim invoice.html
Paste the template and save it. Put any PNG named logo.png next to it:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Invoice 2026-118</title>
<style>
body { font-family: "DejaVu Sans", sans-serif; padding: 24px; }
h1 { color: #2c3e50; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ccc; padding: 8px; text-align: left; }
th { background: #f4f4f4; }
</style>
</head>
<body>
<img src="logo.png" width="64" alt="logo">
<h1>Invoice 2026-118</h1>
<p>Billed to: Example Corp</p>
<table>
<tr><th>Item</th><th>Price</th></tr>
<tr><td>VPS hosting (12 months)</td><td>$240.00</td></tr>
<tr><td>Managed backups</td><td>$60.00</td></tr>
<tr><td><strong>Total</strong></td><td><strong>$300.00</strong></td></tr>
</table>
</body>
</html>
Convert it with the plain two-argument form first:
wkhtmltopdf invoice.html invoice.pdf
It writes a PDF, but without the logo, and exits with status 1:
Warning: Blocked access to file /home/josphat/invoices/logo.png
Error: Failed to load about:blank, with network status code 301 and http status code 0 - Protocol "about" is unknown
Exit with code 1 due to network error: ProtocolUnknownError
Since 0.12.6, wkhtmltopdf blocks local file access by default. Scripts that worked on older builds break here, and a cron job that checks the exit code will report a failure every night. Allow it explicitly for templates you control:
wkhtmltopdf --enable-local-file-access invoice.html invoice.pdf
pdfimages -list invoice.pdf
pdfinfo invoice.pdf | grep -E "Pages|Page size|Producer"
The logo is now embedded, and the producer line shows the Qt build doing the work:
page num type width height color comp bpc enc interp object ID x-ppi y-ppi size ratio
--------------------------------------------------------------------------------------------
1 0 image 128 128 rgb 3 8 image no 6 0 240 240 291B 0.6%
Producer: Qt 4.8.7
Pages: 1
Page size: 595 x 842 pts (A4)
Qt 4.8.7 is the engine inside the patched build, and it explains most of wkhtmltopdf’s CSS limits. Flexbox, grid and CSS variables do not exist there. Never pass --enable-local-file-access when the HTML comes from users, since it lets the HTML load any local file the process can read.
Add page numbers, margins and a PDF title
Headers and footers are the main reason to insist on the patched build. Page size, orientation and margins are flags too:
wkhtmltopdf --enable-local-file-access \
--page-size A4 --orientation Portrait \
--margin-top 20mm --margin-bottom 20mm \
--title "Invoice 2026-118" \
--footer-center "Page [page] of [topage]" --footer-font-size 8 \
invoice.html invoice-styled.pdf
Check that the title and the footer text made it into the file:
pdfinfo invoice-styled.pdf | grep -E "Title|Pages"
pdftotext invoice-styled.pdf - | grep "Page 1"
Both are present:
Title: Invoice 2026-118
Pages: 1
Page 1 of 1
The flags you will reach for most often:
| Flag | What it does | Example |
|---|---|---|
| –page-size | Paper size (A4, A3, Letter, Legal) | –page-size Letter |
| –orientation | Portrait or Landscape | –orientation Landscape |
| –margin-top / -bottom / -left / -right | Page margins in mm, cm or in | –margin-top 20mm |
| –header-center, –footer-center | Header or footer text; [page] and [topage] expand to numbers | –footer-center “Page [page] of [topage]” |
| –header-html, –footer-html | Use an HTML file as the header or footer | –footer-html footer.html |
| –title | PDF metadata title | –title “Invoice 2026-118” |
| –javascript-delay | Milliseconds to wait for scripts (default 200) | –javascript-delay 1000 |
| –enable-local-file-access | Allow file:// images, CSS and fonts | –enable-local-file-access |
| -q | Silence the progress bars in logs | -q |
Render a web page URL to PDF
Pass a URL instead of a file. The -q flag keeps the progress bars out of your logs:
wkhtmltopdf -q https://example.com example.pdf
ls -lh example.pdf
pdftotext example.pdf - | head -3
The page text is in the PDF:
-rw-r--r-- 1 josphat josphat 5.5K Oct 8 10:32 example.pdf
This domain is for use in documentation examples without
needing permission. This is not a service; avoid relying on
it for testing and monitoring purposes.
Static pages are fine. Single-page apps are not. The 2012 WebKit cannot parse modern JavaScript, so React and Vue dashboards come out blank or half drawn no matter how long --javascript-delay waits. In our tests even ES2020 optional chaining failed. Those pages need a real browser, such as Chrome headless with –print-to-pdf or Playwright.
Render HTML to an image with wkhtmltoimage
The same package ships wkhtmltoimage, which writes PNG, JPG or SVG. Width is yours to set, and height follows the content:
wkhtmltoimage --enable-local-file-access --format png --width 1024 invoice.html invoice.png
file invoice.png
The result is a 1024 pixel wide PNG:
invoice.png: PNG image data, 1024 x 392, 8-bit/color RGBA, non-interlaced
Call wkhtmltopdf from Python with pdfkit
Most applications reach wkhtmltopdf through a wrapper. For Python that is pdfkit, which maps a dictionary onto the CLI flags. Install it in a virtual environment, since Ubuntu and Debian block system-wide pip installs:
sudo apt install -y python3-venv
python3 -m venv ~/pdfkit-env
~/pdfkit-env/bin/pip install pdfkit
Create the render script in the invoices directory:
vim ~/invoices/render.py
Add the following. The None value turns a flag without an argument on:
import pdfkit
options = {
"page-size": "A4",
"margin-top": "20mm",
"margin-bottom": "20mm",
"footer-center": "Page [page] of [topage]",
"encoding": "UTF-8",
"title": "Invoice 2026-118",
"enable-local-file-access": None,
}
pdfkit.from_file("invoice.html", "invoice-python.pdf", options=options)
print("Wrote invoice-python.pdf")
Run it and inspect the result:
cd ~/invoices
~/pdfkit-env/bin/python render.py
pdfinfo invoice-python.pdf | grep -E "Title|Pages"
pdfkit found the binary in /usr/local/bin without extra configuration:
Wrote invoice-python.pdf
Title: Invoice 2026-118
Pages: 1
pdfkit.from_string() and pdfkit.from_url() take an HTML string or a URL instead of a file, which suits Jinja2, Django and Flask templates. pdfkit has not shipped a release since November 2021 either, so plan the migration for both at once.
Replace wkhtmltopdf: pick the successor for your job
The install above buys time. It does not fix the frozen engine or the open CVE. We benchmarked the replacements on the same invoice, report and JavaScript dashboard, and the full wkhtmltopdf alternatives comparison has the numbers. The short version, with a tested setup guide for each:
| Your wkhtmltopdf job | Move to | Why |
|---|---|---|
| Invoices and reports from your own templates | WeasyPrint | Full CSS paged media: running headers, page counters, footnotes. No browser needed. |
| A PDF service several apps call | Gotenberg in Docker | HTTP API over Chromium and LibreOffice, blocks file:// reads by default |
| JavaScript-heavy pages and dashboards | Playwright or Puppeteer | Real Chromium, header and footer templates, waits for the page to finish |
| Shell scripts and cron jobs | Chrome headless –print-to-pdf | One command, and the headless shell build matched wkhtmltopdf’s 0.29 s per-run time in our tests |
| PHP applications | Dompdf | Pure PHP via Composer, no external binary |
Header and footer flags have direct equivalents in most of them, so the migration is mostly moving --footer-center into an @page rule or a footer template. If Odoo or another platform calls wkhtmltopdf for you, keep this patched build until the platform ships its own replacement, and keep untrusted HTML away from it.
Thanks for a nice explanation. it helped me a lot
was facing an issue while installing the lib.
The following packages have unmet dependencies:
wkhtmltox : Depends: libpng12-0 but it is not installed
Depends: libssl1.0.0 but it is not installed
Depends: xfonts-75dpi but it is not installed
Depends: xfonts-base but it is not installed
but fixed it by running
apt –fix-broken install
When I run that I get E: Invalid operation –fix-broken
just replace ‘–’ to ‘–‘
Thanks, was looking for this everywhere!
really help me, thanks a lot
THANKS
Gracias por la ayuda, lo solucione en Odoo 13 con servidor Debian, el eror era, que al imprimir no mostraba el encabezado ni el pie de pagina, ademas de no ostrar el logo.
Gracias
wkhtmltopdf can be installed with apt in ubuntu 20.04