Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Choose the renderer that matches your input. For controlled HTML templates and mostly CSS 2.1, use Dompdf; for print-oriented documents with features such as page numbers and headers, consider mPDF; for a modern webpage whose browser appearance matters, use isolated headless Chrome. TCPDF and its successor tc-lib-pdf suit workflows that need direct PDF generation and standards-related tooling. Treat wkhtmltopdf as a legacy option, and never run it on unsanitized input.
Choose a PHP-to-PDF method
“HTML to PDF” can mean two different jobs: laying out your own template as a document, or printing an existing, possibly JavaScript-driven website. That distinction matters more than the language calling the renderer. A PHP library can be convenient for a controlled invoice template, but it is not automatically a browser. If the source depends on modern CSS, JavaScript, or browser-specific behavior, a browser engine is the closer match.
| Option | Rendering model | Best fit | Important limitation |
|---|---|---|---|
| Dompdf | PHP layout engine; mostly CSS 2.1 | Invoices, reports, controlled HTML templates | Modern CSS and browser behavior are limited. Remote fetching is off by default and requires careful configuration. |
| mPDF | PHP library generating PDF from UTF-8 HTML | Print-style documents with pagination, headers, footers, page numbers, bookmarks, barcodes, or tables of contents | The manual describes the project as dated for modern CSS; templates may need mPDF-specific adjustments. mPDF Manual |
| TCPDF / tc-lib-pdf | Direct PDF generation using a documented HTML/CSS subset, not a browser engine | Deterministic in-process output, font tooling, and PDF/A, PDF/X, or PDF/UA workflows | Browser-only layout and JavaScript are not provided; the supported CSS subset is documented by the project. |
| Headless Chrome | Real browser engine | Modern webpages, JavaScript-driven content, and closer visual parity with a browser | Requires Chromium deployment, process isolation, resource controls, and operational planning. |
| wkhtmltopdf | Older WebKit command-line renderer | Existing legacy deployments with controlled input | The official stable series is 0.12.6, dated 11 June 2020, and the project warns of severe risk from untrusted HTML. wkhtmltopdf downloads |
For a new PHP project, start with Dompdf only if its layout limits fit. Choose a browser renderer for a real webpage that must look like the browser version. mPDF and TCPDF/tc-lib-pdf are alternatives when their print or PDF-generation features fit better than browser fidelity. The Dompdf project describes it as an HTML-to-PDF converter; that does not mean it supports every browser feature.
Convert a controlled HTML template with Dompdf
Dompdf is the straightforward starting point when your application owns the markup and can design for its rendering model. Install it with Composer, load Composer’s autoloader, pass UTF-8 HTML to loadHtml(), set the paper size and orientation, render, and stream the result or save it. Here is a minimal endpoint that accepts no user-supplied HTML and streams a PDF:
#1 Best Overall
<?php
require __DIR__ . '/vendor/autoload.php';
use DompdfDompdf;
use DompdfOptions;
$options = new Options();
// Keep remote fetching off unless this document genuinely needs remote assets.
$options->set('isRemoteEnabled', false);
$dompdf = new Dompdf($options);
$html = '<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
body { font-family: DejaVu Sans, sans-serif; font-size: 12px; }
h1 { font-size: 22px; }
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #777; padding: 6px; text-align: left; }
</style>
</head>
<body>
<h1>Monthly report</h1>
<p>Generated by the application.</p>
<table><tr><th>Item</th><th>Total</th></tr>
<tr><td>Example</td><td>42</td></tr></table>
</body>
</html>';
$dompdf->loadHtml($html, 'UTF-8');
$dompdf->setPaper('A4', 'portrait');
$dompdf->render();
$dompdf->stream('report.pdf', ['Attachment' => true]);
Install the dependency with composer require dompdf/dompdf. For an application that should keep a generated file rather than send it as a download, replace the final stream call with file_put_contents($path, $dompdf->output()); and choose a path that is not publicly writable or directly controlled by a request parameter.
Prepare the HTML for a PDF layout engine
- Use a simple, explicit document structure and test CSS against the renderer rather than assuming the browser’s layout rules apply.
- Design page breaks and long tables deliberately. Check the final PDF for split rows, clipped content, unexpected blank pages, and missing images.
- Register or select fonts intentionally. Verify glyph coverage for the languages and symbols the document needs; visual fallback in a browser does not guarantee the same PDF output.
- Keep remote assets disabled where possible. If the template requires remote images or stylesheets, enable fetching only with an explicit host allowlist and protections against access to internal services.
- Create a fresh Dompdf instance for each document. The project documents that parser and rendering artifacts can persist if a single instance is reused for multiple documents.
Use mPDF for print-oriented documents
mPDF takes UTF-8 HTML through WriteHTML() and emits a PDF through Output(). Its print features can make it a sensible fit when the document needs pagination controls, headers, footers, page numbering, bookmarks, barcodes, or a table of contents. Expect to tune HTML and CSS for mPDF rather than treating it as a drop-in modern browser.
<?php
require __DIR__ . '/vendor/autoload.php';
$mpdf = new MpdfMpdf();
$html = '<!doctype html><html><head><meta charset="UTF-8"></head>'
. '<body><h1>Report</h1><p>UTF-8 document content.</p></body></html>';
$mpdf->WriteHTML($html);
$mpdf->Output('report.pdf', MpdfOutputDestination::DOWNLOAD);
As with any renderer, validate and sanitize HTML before it reaches the library. The mPDF HTML-or-PHP guidance explicitly says mPDF is not intended to receive outside HTML/CSS. Do not pass arbitrary user markup to it simply because the conversion happens inside PHP.
Render an existing webpage with a browser engine
If the page relies on JavaScript, contemporary CSS, or the browser’s layout behavior, use headless Chrome rather than forcing the page into a PHP HTML/CSS subset. Run Chromium in an isolated process with only the permissions the job needs. Wait for the page’s fonts, images, and client-rendered content to settle before printing; otherwise a technically successful PDF can still be visually incomplete.
Rank #3
The operational design is part of the implementation: constrain navigation and network destinations, disallow unnecessary file access, apply process and memory limits, set a deadline, and limit output size. If the page is private, account for authentication and session state without exposing credentials in logs or allowing a renderer to reach unrelated internal services. Test representative pages because a local template and a live site can fail in different ways.
When a webpage URL is not a document template
A URL capture has different edge cases from rendering a string you control. The source may redirect, show a consent dialog, require login, load content after initial navigation, or refuse automated access. The PDF’s appearance also depends on the page’s current state and the resources the renderer can reach. Use a browser workflow when you need its JavaScript and CSS behavior, but treat each URL as untrusted input and enforce an allowlist when the URL comes from a user or external system.
When to use TCPDF or tc-lib-pdf
TCPDF and tc-lib-pdf are appropriate when you want direct PDF generation and need to work within a documented subset rather than rely on a full browser. Their HTML entry points include methods such as addHTMLCell() and getHTMLCell(). The engine applies its supported cascade, selectors, box model, tables, typography, floats, and paged-media controls while handling page and region breaks. It does not provide JavaScript execution or browser-only layout. Confirm the exact supported behavior and package setup in the documentation for the version you install, especially for PDF/A, PDF/X, PDF/UA, signatures, or multilingual font requirements.
Why wkhtmltopdf is usually a legacy choice
wkhtmltopdf may remain in an existing deployment where inputs are controlled and the team can isolate the renderer. It is an older WebKit command-line tool; the project’s downloads page lists 0.12.6 as the stable series dated 11 June 2020. More importantly, the project warns that untrusted HTML can lead to complete server takeover. Do not run it against user-provided HTML or URLs without strict sanitization and strong isolation. For a new deployment, weigh that risk and maintenance posture against a maintained PHP renderer or a deliberately isolated browser workflow.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Security, reliability, and output checks
- Sanitize untrusted markup. HTML sanitization is not optional when users can influence markup, CSS, links, or resources. Prefer a constrained template with escaped values over accepting arbitrary HTML.
- Control network access. Remote images and stylesheets can create server-side request risks. Keep remote fetching off unless required; if enabled, permit only named public hosts and block private, loopback, and link-local destinations.
- Constrain execution. Apply timeouts, memory and output-size limits, and process isolation to browser and command-line renderers. Do not let the renderer inherit broader application permissions than it needs.
- Pin dependencies. Pin Composer packages and external browser binaries, then re-check compatibility when upgrading either side.
- Test real documents. Include page breaks, long tables, images, SVG, links, headers and footers, print colors, and multilingual text in a representative test set.
- Check the generated file. Confirm it opens, has the expected page count and dimensions, includes fonts and images, and does not truncate content. A successful process exit alone does not verify document correctness.
Troubleshoot common conversion failures
| Symptom | Likely cause | What to check or change |
|---|---|---|
| Modern layout differs from the browser | The chosen PHP engine supports a smaller CSS model than a browser. | Reduce the template to supported CSS, test the renderer’s documented subset, or move to headless Chrome if browser fidelity is required. |
| Remote image or stylesheet is missing | Remote fetching is disabled or the renderer cannot safely reach the asset. | Prefer local or embedded assets. If remote access is necessary, enable it narrowly and use an explicit host allowlist; verify the resource is reachable from the renderer. |
| Content is absent although the webpage opened | Client-side rendering or fonts and images were not ready when printing began. | In the browser workflow, wait for the relevant selector or page resources before printing, and confirm the page state in the isolated browser process. |
| PDF characters are missing or replaced | The selected or embedded font lacks required glyphs, or font registration is incomplete. | Register a font with the needed character coverage and test the actual language and symbols in the produced PDF. |
| Later Dompdf documents render unexpectedly | A reused instance can retain parser or rendering artifacts. | Instantiate Dompdf once per document instead of reusing the same instance. |
| Conversion stalls or creates an unexpectedly large file | Slow or unbounded resource loading, oversized images, or complex pages can consume time and memory. | Set execution and output limits, constrain network requests, reduce source asset size, and isolate the job so a failed conversion cannot exhaust the application process. |
| Untrusted URL causes unexpected server access | The renderer can make outbound requests or navigate to a destination the application should not expose. | Validate and allowlist URLs before rendering, block internal address ranges at the network layer, and disable remote resource access where it is unnecessary. |
Or skip the browser setup
If your goal is to capture a public webpage as a PDF without operating a browser renderer yourself, ScreenshotNeo is a website screenshot API and MCP server. Its documented offer includes clean captures that accept consent banners like a visitor and remove more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, with page-verdict and billed headers in the response. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for AI agents. One GET request takes a URL; the service can return a screenshot or PDF. Consult the ScreenshotNeo documentation for PDF output options and API details.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
This supplied request saves a WebP capture; choose and configure PDF output using the current API documentation rather than assuming an undocumented parameter. ScreenshotNeo includes 1,000 shots per month free with no card, and paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month with no card.
Frequently Asked Questions
Can a PHP PDF library convert an HTML string and a remote URL in the same way?
No. A PHP library can lay out supplied markup, while a remote webpage may depend on navigation, JavaScript, browser state, and network-loaded assets. Choose based on the actual source and rendering requirements.
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 problemsShould I reuse one renderer instance to process a batch?
For Dompdf, create a new instance for each document; its project documentation warns that parser and rendering artifacts can persist across documents.
Is a successful conversion enough to prove the PDF is correct?
No. Open the output and verify page count, layout, images, fonts, and content boundaries using representative documents.
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.

