Use a Composer-installed HTML-to-PDF renderer, not a browser print command. For a straightforward PHP implementation, Dompdf provides a short loadHtml() → render() → stream() flow. mPDF is another practical choice, especially for UTF-8 HTML, but it has its own extension and temporary-directory requirements. Neither is universally best: the right choice depends on your CSS, fonts, assets, PHP release, and deployment environment.
Choose the renderer before writing code
HTML-to-PDF libraries do not render exactly like Chrome or Firefox. Inventory the template first: does it use flexbox, CSS Grid, web fonts, long tables, SVG, remote images, or user-supplied markup? Then verify the selected package release against your PHP version and enabled extensions. Development-branch documentation can differ from stable-release requirements, so pin a version and read that version’s documentation.
| Constraint | Dompdf | mPDF |
|---|---|---|
| Installation | composer require dompdf/dompdf |
composer require mpdf/mpdf |
| Rendering model | Mostly CSS 2.1-oriented PHP renderer | UTF-8 HTML renderer with its own layout behavior |
| Important layout caveats | No flexbox or CSS Grid; table rows must fit on one page; raw inline SVG has limitations | Validate the actual template and pagination; do not assume browser-equivalent CSS |
| Runtime checks | Package-release PHP requirements; cURL or allow_url_fopen for remote assets |
mbstring and GD; advanced features may need zlib, bcmath, or XML |
| Filesystem/operations | Local files must stay inside configured chroot paths |
Use a dedicated writable temporary directory; external HTTP fetching can fail on PHP’s single-threaded development server |
The table describes documented capabilities and requirements, not a speed or quality benchmark. Measure memory, latency, and output using your own documents if those factors affect a production decision.
Convert HTML to PDF with Dompdf
1. Install and bootstrap
From your project directory, install Dompdf with Composer:
#1 Best Overall
composer require dompdf/dompdf
Include Composer’s autoloader. Create a new Dompdf instance for each document; parser and rendering state should not be reused between unrelated documents.
2. Render a complete document
<?php
require __DIR__ . '/vendor/autoload.php';
use DompdfDompdf;
use DompdfOptions;
$html = '<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<style>
@page { margin: 22mm 18mm; }
body { font-family: DejaVu Sans, sans-serif; font-size: 11pt; line-height: 1.45; }
h1 { font-size: 22pt; margin: 0 0 12pt; }
table { width: 100%; border-collapse: collapse; }
th, td { border: 0.2mm solid #999; padding: 5pt; }
</style>
</head>
<body>
<h1>Invoice</h1>
<p>Generated from trusted, escaped application data.</p>
</body>
</html>';
$options = new Options();
$options->set('isRemoteEnabled', false);
$pdf = new Dompdf($options);
$pdf->loadHtml($html, 'UTF-8');
$pdf->setPaper('A4', 'portrait');
$pdf->render();
$pdf->stream('invoice.pdf', ['Attachment' => true]);
The paper and orientation can be changed, for example to letter and landscape. In a web response, send no HTML before stream(); accidental whitespace, warnings, or a UTF-8 byte-order mark can corrupt the PDF response. For an API or queue worker, obtain the rendered bytes using the package’s documented output method and store them rather than streaming them directly.
3. Load images and styles safely
Dompdf requires isRemoteEnabled to be true for remote assets, and PHP must have cURL or allow_url_fopen enabled. Local references must remain within the configured chroot. Prefer local, allowlisted assets whenever possible:
$options = new Options();
$options->set('chroot', __DIR__ . '/public/pdf-assets');
$options->set('isRemoteEnabled', false);
If a trusted, fixed remote image is unavoidable, enable remote loading only for that use case and enforce URL allowlists. Never let arbitrary user HTML choose unrestricted filesystem paths or fetch internal network addresses.
Rank #2
Convert HTML to PDF with mPDF
1. Install the package and verify extensions
composer require mpdf/mpdf
mPDF’s documented baseline requires the mbstring and GD extensions. Depending on the features you use, zlib, bcmath, or XML may also be required. Check the exact package release and your PHP image before deployment.
2. Write UTF-8 HTML and output the file
<?php
require __DIR__ . '/vendor/autoload.php';
$mpdf = new MpdfMpdf([
'tempDir' => __DIR__ . '/var/mpdf-temp',
]);
$html = '<!doctype html>
<html lang="en">
<head><meta charset="utf-8"></head>
<body>
<h1>Report</h1>
<p>UTF-8 text: café, Ελληνικά, हिन्दी, 日本語.</p>
</body>
</html>';
$mpdf->WriteHTML($html);
$mpdf->Output(__DIR__ . '/var/report.pdf', MpdfOutputDestination::FILE);
For a browser download, use the package’s inline or download destination instead of FILE. Create the temporary directory before the request, make it writable by the PHP user, and dedicate it to mPDF: mPDF cleans old temporary files there. Do not point it at a shared directory containing unrelated application data.
mPDF warns that fetching external HTTP resources can fail on single-threaded servers such as PHP’s built-in server. Test remote assets behind Apache or nginx with PHP-FPM, or download approved assets before rendering.
Prepare HTML that survives PDF pagination
Use a PDF-friendly CSS subset
- Use explicit widths, margins, and page sizes rather than relying on viewport units.
- Prefer normal document flow and tables for tabular data.
- Do not depend on flexbox or CSS Grid when targeting Dompdf.
- Keep table rows small enough to fit on one page; split very large records into separate rows or sections.
- Use print-oriented rules such as
@page, and test headings, footers, images, and page breaks with representative content.
Make fonts and encoding deliberate
Set a UTF-8 meta tag and pass UTF-8 input. Built-in PDF base fonts have limited character coverage. If your document contains accented, Cyrillic, Arabic, Indic, or CJK text, provide a font with the required glyphs and configure it according to the renderer’s documentation. Test real names and symbols, not only ASCII placeholders.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Escape application data
Generate the template separately from the data, escape text for the HTML context, and validate URLs and attributes. User input passed to mPDF must be vetted and sanitized beyond ordinary browser-level sanitization. The same rule applies to Dompdf: HTML-to-PDF processing can read files or make network requests if you configure it to do so.
A production workflow
- Pin the dependency. Commit
composer.lockand verify the package’s PHP and extension requirements in CI. - Build a deterministic template. Keep CSS and approved assets in known locations; avoid runtime dependence on third-party pages.
- Render in isolation. Create one renderer instance per document and set an explicit paper size and orientation.
- Validate the output. Check HTTP headers, PDF readability, page count, fonts, images, table breaks, and non-Latin text.
- Apply limits. Bound HTML size, image dimensions, render time, and memory. Queue large reports instead of blocking a web request.
- Observe failures. Log the package version, template identifier, render duration, memory peak, and sanitized error details; never log secrets or untrusted HTML.
Troubleshooting common failures
“Class not found” after installation
The request is probably not loading Composer’s autoloader, or the command ran in a different project directory. Confirm vendor/autoload.php exists, deploy the vendor directory or run Composer during deployment, and use the same PHP binary for Composer and the web worker.
Images or CSS are missing
Check URL resolution, file permissions, and the renderer’s resource policy. For Dompdf, remote loading is off unless isRemoteEnabled is enabled, and local files outside chroot are rejected. Convert relative paths to known local paths or allowlist specific remote hosts. For mPDF, test external fetching under the production web server rather than PHP’s built-in server.
Modern layout collapses
This is expected when the template relies on browser-only features. Replace flexbox or Grid with supported block/table layout for Dompdf, simplify selectors, and introduce explicit widths. If browser-level CSS fidelity is mandatory, a browser automation service may be a better architecture than a PHP-only renderer.
Free tools Windows power users keep installed
One-click scans. No signup required.
Rank #4
Text is blank, boxed, or garbled
Confirm UTF-8 at every stage, embed or configure a font covering the characters, and test that the deployed machine can read the font files. A font available on your laptop may not exist in the container or server.
Tables overlap or split badly
Reduce cell content, set predictable column widths, and test long values. Dompdf documents that table rows must fit on one page, so split oversized records rather than expecting a row to break cleanly.
Permission or temporary-file errors
Ensure the PHP worker can write the configured mPDF temporary directory and that it is dedicated to mPDF. Check container read-only mounts, SELinux/AppArmor policy, and cleanup jobs before changing permissions broadly.
The downloaded PDF is corrupt
Remove output before the PDF headers, disable display of PHP warnings in production, and return the correct content type. Save the bytes to disk first when debugging, then inspect the response for stray text or a partial render caused by a timeout.
When a URL, rather than a PHP template, is the source
If the document already exists as a public web page, a screenshot/PDF API can avoid maintaining a browser-rendering setup. ScreenshotNeo accepts one GET request and can return a PDF; it also supports waits, custom headers and cookies, JavaScript, page ranges, paper size, margins, and landscape output. It is not a replacement for sanitizing HTML you generate inside PHP, but it is useful when the input is a URL and browser behavior matters.
Or skip the browser setup
Call ScreenshotNeo’s endpoint with your page URL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
For PDF output, use the PDF options described in the ScreenshotNeo documentation and set the target URL to your own page. The service accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP server gives Claude, Cursor, and other MCP clients take_screenshot, get_page_info, and capture_pdf tools.
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is included on every plan. Create a free ScreenshotNeo account.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Cost, reliability, and security decisions
- Cost: Dompdf and mPDF run in your PHP infrastructure, so account for CPU, memory, storage, and operational work rather than assuming rendering is free.
- Reliability: There is no documented universal speed advantage here. Benchmark your largest HTML, worst-case images, and longest tables on the exact production image.
- Isolation: Treat HTML and resource URLs as potentially dangerous. Use allowlists, filesystem chroots, private-network protections, size limits, and timeouts.
- Reproducibility: Pin versions, package fonts and assets, and keep a small visual regression corpus containing multilingual text, long tables, images, and page breaks.
Frequently Asked Questions
Can I convert HTML to PDF without installing a PHP library?
Yes, a URL-to-PDF service can render an existing web page, but PHP-generated or private HTML still needs a controlled handoff, authentication strategy, and resource policy. A Composer renderer keeps the conversion inside your application.
Which library should I choose for a new project?
Choose after testing the real template. Dompdf is a concise option for supported CSS; mPDF is a documented alternative with UTF-8 handling and additional extension and temporary-directory requirements.
Why does my PDF differ from the browser preview?
PHP renderers are not full browser engines. Unsupported CSS, font availability, pagination rules, and asset-loading policy can all change the result.
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.
Recommended Free Tools

