Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
For a clean, modest HTML document, Dompdf is usually the quickest PHP solution: install it with Composer, load the HTML, render, and stream the PDF. If your page depends on browser CSS, JavaScript, or complex responsive layout, have PHP call a Chromium-based renderer instead. The right choice depends on the markup you actually generate, pagination requirements, server dependencies, and how much untrusted input you accept.
What “HTML to PDF in PHP” actually means
There are two fundamentally different architectures:
- A PHP renderer parses HTML and CSS inside your PHP process. Dompdf and tc-lib-pdf follow this model.
- A browser renderer starts or contacts a real browser engine. PHP acts as the integration layer, while Chromium, Puppeteer, wkhtmltopdf, or an HTTP service does the rendering.
A PHP renderer is simpler to deploy but supports a narrower CSS and layout model. A browser renderer generally matches modern web pages more closely, at the cost of an external runtime, binary, or service that must be installed, secured, monitored, and upgraded.
Choose the renderer from your document’s requirements
| Approach | Best fit | Important trade-offs |
|---|---|---|
| Dompdf | Invoices, letters, reports, and templates using conservative HTML and CSS | Mostly CSS 2.1 compliant with selected CSS3 features; table cells are not pageable, so a row must fit on one page. Requires PHP 7.1+, DOM, MBString, and Composer dependencies according to the project documentation at dompdf.github.io. |
| tc-lib-pdf HTML rendering | A direct PHP library when the document fits its documented subset | It “renders a subset of HTML … and CSS … directly, with no browser engine involved.” Verify supported tags, fonts, and pagination in the TCPDF HTML/CSS documentation. |
| Browser renderer through PHP | Modern CSS, JavaScript-generated content, and browser-like layout | Needs Chromium/Node/Puppeteer, a maintained binary, or a separately operated service. PHP wrappers are integrations, not browser engines written in PHP. |
| wkhtmltopdf-based wrapper | Existing systems where its WebKit output already matches the templates | The project lists 0.12.6, released June 11, 2020, as its stable series. Treat that as a dated project fact, not proof of current maintenance. Sanitize untrusted HTML and JavaScript before rendering. |
Compare candidates using the real document: CSS fidelity, long-table pagination, Unicode and font coverage, image handling, PHP and operating-system compatibility, external-process requirements, input trust boundaries, licensing, and package maintenance. No source establishes one library as universally best.
#1 Best Overall
Fastest implementation: Dompdf
Install it
In an application using Composer, run:
composer require dompdf/dompdf
Check the current release documentation before deployment. The project page documents PHP 7.1 or newer plus the DOM and MBString extensions and Composer packages such as php-font-lib and php-svg-lib.
Render and download a PDF
<?php
require __DIR__ . '/vendor/autoload.php';
use DompdfDompdf;
use DompdfOptions;
$options = new Options();
$options->set('isRemoteEnabled', false); // Enable only if your design needs approved remote assets.
$options->set('defaultFont', 'DejaVu Sans');
$dompdf = new Dompdf($options);
$html = '<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<style>
@page { size: A4; margin: 18mm; }
body { font-family: DejaVu Sans, sans-serif; color: #222; }
h1 { font-size: 22px; }
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #bbb; padding: 6px; }
thead { display: table-header-group; }
</style>
</head>
<body>
<h1>Invoice 1042</h1>
<p>Generated by the PHP application.</p>
</body>
</html>';
$dompdf->loadHtml($html, 'UTF-8');
$dompdf->setPaper('A4', 'portrait');
$dompdf->render();
$dompdf->stream('invoice-1042.pdf', ['Attachment' => true]);
Use Attachment => false to display the PDF inline in a browser. For an API response, obtain $dompdf->output() and send your own Content-Type: application/pdf and Content-Disposition headers.
Make the input safe and predictable
- Escape user values with
htmlspecialchars($value, ENT_QUOTES, 'UTF-8')before inserting them into a template. - Prefer a complete, well-formed HTML document. Dompdf documents that malformed markup can produce unexpected output.
- Keep remote assets disabled unless you need them. If enabled, allow only known hosts and schemes and avoid accepting arbitrary URLs.
- Bundle and register the fonts your PDF needs, especially for non-Latin scripts; test the actual production font files.
- Design long table rows to fit on one page. Dompdf’s documented table behavior does not page a cell across pages; split data deliberately or use another renderer when a single row can exceed a page.
When a browser engine is the better answer
Use a browser-based path when the template depends on flexbox or grid behavior that your PHP renderer does not reproduce, client-side JavaScript, web-font loading, complex SVG, or CSS that changes frequently with a web application. A common architecture is PHP calling a Node/Puppeteer and Chromium process, or sending the HTML to an HTTP renderer such as a Chromium-based service. This adds process startup, sandboxing, executable management, timeouts, and observability work. Run the browser with the least privilege practical, restrict outbound network access, and place limits on HTML size, navigation time, and concurrency.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Do not switch solely because a browser sounds more capable. Render representative invoices, reports, long tables, images, page breaks, and non-Latin text in the target environment before committing. A browser can still produce different pagination when fonts, viewport, network timing, or installed Chromium versions change.
Rank #2
tc-lib-pdf and other direct PHP options
tc-lib-pdf is appropriate when you want a PHP-native implementation and your templates fit its documented HTML/CSS subset. It does not execute a browser engine, so verify every required element, CSS property, font, image type, and page-break rule against the current documentation at tcpdf.org/docs/html-css/. The TCPDF comparison page records package and PHP requirement details checked on August 31, 2026; those details can change, so inspect the current package metadata during implementation.
Pagination, assets, and fonts that commonly break PDFs
Long tables
Use repeated table headers where your renderer supports them, keep rows reasonably small, and insert explicit section breaks between logical groups. If a description or embedded image can make one row taller than a page, split that record before rendering.
Images and remote resources
Local paths, data URIs, and remote URLs are not interchangeable. Confirm that the renderer can read the chosen source, that PHP has permission to access local files, and that production firewalls permit required hosts. Never grant broad filesystem or network access merely to fix a missing image.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsUnicode and fonts
Declare UTF-8, supply a font with the required glyphs, and test characters such as accented names, currency symbols, Arabic, CJK, and emoji separately. A missing glyph can appear as a square even when the HTML source is correct.
Page size and orientation
Set the paper size and margins in one place and keep CSS dimensions consistent with them. A4 and Letter are not interchangeable; state the target explicitly for each generated document.
Security boundaries for untrusted HTML
Treat HTML supplied by users as code-like input. Sanitize tags, attributes, URLs, CSS, and scripts before handing it to any renderer. The wkhtmltopdf project specifically warns that unsanitized HTML and JavaScript can allow server takeover; apply that warning to wkhtmltopdf deployments and to any architecture where HTML can reach a privileged browser. Run renderers in an isolated worker or container when feasible, use restrictive network and filesystem policies, and enforce CPU, memory, page-count, and timeout limits.
Production checklist
- Record the exact PHP version, extensions, renderer package version, fonts, and operating system in deployment documentation.
- Render fixtures covering short and multi-page documents, long tables, images, page breaks, Unicode, and failure cases.
- Compare generated PDFs using text extraction and visual review; avoid relying only on an HTTP 200 response.
- Log render duration, output size, page count, renderer errors, and input identifiers without logging sensitive document contents.
- Queue expensive browser renders and cap concurrency so a burst cannot exhaust web workers.
- Set deterministic timeouts for asset loading and external processes; return a useful retryable error to callers.
- Pin dependencies, review security advisories, and retest after upgrading PHP, fonts, Chromium, or the PDF library.
Troubleshooting common failures
“Class Dompdf\Dompdf not found”
Composer’s autoloader was not included or dependencies were installed in a different release directory. Run Composer in the application root and require vendor/autoload.php from the deployed path.
Blank or partly blank output
Check malformed HTML, unsupported CSS, fatal renderer errors, missing fonts, and inaccessible images. Save the exact HTML fixture and render it outside the request path to isolate application data from renderer behavior.
Rank #4
Images or CSS are missing
Verify absolute versus local paths, file permissions, URL schemes, and remote-resource settings. Allow only the hosts and directories the document genuinely needs.
Rows overlap or jump to the next page
Reduce oversized cells, simplify nested tables, and add deliberate page breaks. With Dompdf, remember that a table cell cannot be split across pages.
Browser wrapper cannot start
Confirm the executable or Node runtime exists for the service user, its sandbox permissions are valid, and the configured timeout allows startup. Capture stderr and test the same command as the production account.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11wkhtmltopdf works locally but not on Windows production
The PHP manual documents that wkhtmltox.dll must be available on the Windows PATH. Also verify architecture, binary permissions, and the exact installed version.
Non-Latin text is corrupted
Ensure the HTML declares UTF-8 and that a font containing the needed glyphs is installed, embedded, or registered for the selected renderer.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server when you need a rendered page or PDF without maintaining a browser process. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; those cleanup steps can be disabled individually. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.
For a one-call PDF or image response, see the ScreenshotNeo API documentation. cURL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo includes full-page capture, CSS-selector element capture, dark mode, device presets and custom viewports, retina scale, PDF paper and page-range controls, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, timezone and geolocation, transparent backgrounds, resizing, configurable caching, signed links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, usage APIs, and an OpenAPI specification. Its parameter names are compatible with those used by other screenshot APIs, which can simplify migration.
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; all features are available on every plan, and annual billing provides two months free. Create a free ScreenshotNeo account.
Which path should you deploy?
- Choose Dompdf for controlled templates that fit its CSS and pagination model.
- Choose tc-lib-pdf when its documented subset meets your requirements and you want a direct PHP library.
- Choose a browser renderer when browser fidelity is more important than a minimal runtime.
- Choose wkhtmltopdf only after evaluating its dated stable series, security posture, and compatibility with your existing templates.
- Choose a managed API when operating Chromium yourself would add more infrastructure than the document workflow justifies.
Frequently Asked Questions
Can PHP convert an existing public web page directly to PDF?
Yes, but a server-side renderer must be able to fetch the page and its assets, and remote access, authentication, JavaScript, and consent overlays need explicit handling. For a public URL, a browser-based service can be simpler than reproducing the page locally.
Why does the same HTML paginate differently after deployment?
Pagination depends on renderer version, fonts, paper size, margins, viewport, and asset-loading timing. Pin those inputs and test in the production image.
Free tools Windows power users keep installed
One-click scans. No signup required.
Is Dompdf suitable for every modern website?
No. It is mostly CSS 2.1 compliant with selected CSS3 features, not a full browser. Complex layouts or JavaScript-driven pages may require a browser renderer.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

