Skip to main content
Written on 24/10/2026 from the code on chat+ai of LMS_Backend. Keywords for search: wkhtmltopdf, wkhtmltoimage, wkhtmltox, Snappy, WKHTML_PDF_BINARY, WKHTML_IMG_BINARY, dompdf.
Status: CODE DONE ✅ (24/10/2026), SERVER CLEAN-UP PENDING ⚠️. Every PDF the backend makes is rendered by dompdf (barryvdh/laravel-dompdf), a pure-PHP package that composer install brings in. Nothing has to be installed on a server for PDFs anymore. The wkhtmltopdf binaries can be uninstalled from each server once the new backend runs there (see Server clean-up).

At a glance

Why

  • wkhtmltopdf is a system program, installed by hand outside composer on every server and developer machine. A new server without it answered the admin export with a 500.
  • The project is archived upstream (since January 2023): no new releases and no security fixes, and it is built on an old Qt WebKit engine.
  • Only one PDF used it, the admin dashboard export. Invoices, order PDFs and the instructor dashboard export were already rendered by dompdf, so two PDF engines were kept for one file.

What changed in LMS_Backend

Verified: the full backend suite passes (166 tests), and a 5-page sample export (12 months and 52 weeks) was checked page by page: header with logo, company name and export date side by side, table headers repeated on each new page, and the footer on every page without covering any row.

The admin dashboard export

The request and the response did not change, so the apps need nothing.
  • 200: Content-Type: application/pdf and Content-Disposition: attachment; filename=admin_dashboard_20261024_113000.pdf. The A4 report holds the executive summary, the revenue summary (TND), users by role (super admin excluded), users per year, and the monthly and weekly performance tables.
  • 500: { "message": "…" }. Validation errors come back this way too, because the controller’s validate() sits inside its try.

Writing PDF views for dompdf

Keep these in mind for any new or changed PDF view (resources/views/*pdf*.blade.php):
dompdf supports CSS 2.1 plus a few CSS 3 properties. display: flex and display: grid are ignored, so the children stack. Put side-by-side blocks in a <table> (or display: table / table-cell). border-radius, :nth-child and @page margins work.
DejaVu Sans ships with dompdf and covers the • and — characters. It has a regular and a bold weight only: font-weight: 600 finds no match and falls back to the default serif font. Use bold (or 700).
Company logos are R2 URLs. dompdf only downloads them with ->setOption(['isRemoteEnabled' => true]) (every PDF of the backend sets it). A local image must sit inside the project folder (dompdf’s chroot).
<thead> rows repeat on each new page, and a table breaks between rows, never inside one.

Server clean-up

Order matters. Uninstall wkhtmltopdf from a server only after the new backend runs there: the old code still calls the binary, and the admin export would fail with a 500. Do it once per server (DEV, TEST, PROD).
1

Deploy the new backend

Over SSH, as the site’s system user:
If the server deploys through Plesk’s Git integration, pull there and run PHP Composer → Install.
2

Delete the two WKHTML lines from .env

Remove WKHTML_PDF_BINARY and WKHTML_IMG_BINARY (over SSH, or Plesk → Files → .env → Edit in Text Editor). Nothing reads them anymore, so a forgotten line does no harm.
3

Rebuild the caches

Then Plesk → the domain → PHP Settings → Apply, which restarts PHP-FPM so OPcache drops the old files.
4

Test the export

Sign in as an admin, open the dashboard and download the PDF report. It opens with the company logo, the tables, and the footer on every page.
5

Uninstall wkhtmltopdf

Needs root. Follow Uninstall the binaries below.

Uninstall the binaries

First find out how it was installed. The old .env pointed at /usr/local/bin, which is where the official wkhtmltox package from wkhtmltopdf.org puts it.
Then remove it the matching way:
On Debian / Ubuntu, sudo apt autoremove --purge then removes the libraries the package pulled in (X11 libraries, xfonts-*) when nothing else needs them. Read the list before you answer y, and answer n if it names something you don’t recognise. Check that it is gone:

Developer machines

Delete the two WKHTML_* lines from the local .env as well, then run composer install and php artisan optimize:clear.

Rolling back

Not expected to be needed. If it ever is: revert the backend commit, run composer install, reinstall wkhtmltopdf 0.12.6 in /usr/local/bin, and put the two WKHTML_* lines back in .env.