Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To create a PDF from browser content in PHP, generate or fetch the page’s HTML, pass it to a PDF renderer, then stream or save the resulting bytes. Use Dompdf or mPDF for ordinary server-rendered templates, TCPDF when you need explicit cell and page-flow control, and wkhtmltox or headless Chrome when modern CSS, JavaScript, or near-pixel browser fidelity matters. The basic pipeline is:

  1. Build trusted HTML with print-specific CSS.
  2. Render it with a PHP library or browser engine.
  3. Return the PDF inline, as a download, or save it.

This guide shows complete PHP examples, selection criteria, print layout techniques, security controls, deployment considerations, and a hosted alternative when operating a browser runtime is unnecessary.

Choose the rendering approach first

PHP does not contain a native browser-to-PDF engine. A library must interpret your HTML and CSS. The right choice depends on whether you need a lightweight server renderer or the same behavior as a real browser.

Approach Execution model CSS and JavaScript fit Best use Main trade-off
Dompdf PHP renderer CSS 2.1 plus selected CSS3; no browser-level JavaScript Conventional server templates and reports Modern layouts may need adaptation
mPDF PHP renderer HTML/CSS subset, UTF-8-oriented Unicode documents, headers, footers, numbering and tables of contents Templates must be tailored to its renderer
TCPDF/tc-lib-pdf PHP renderer with explicit placement Supported HTML/CSS subset Controlled page flow and precise cell placement More layout code and renderer-specific controls
wkhtmltox QtWebKit-based engine Closer to a browser than PHP-only renderers Pages needing browser-style layout External binary/runtime, sandboxing and process management
Headless Chrome Chromium browser process Best match for modern CSS and JavaScript Pixel-sensitive pages or existing interactive HTML Largest operational footprint

Dompdf describes its renderer as CSS 2.1-oriented and supports stylesheets, presentational attributes, UTF-8, @media print and @page rules. mPDF generates PDFs from UTF-8 HTML and documents headers, footers, page numbering and tables of contents, while noting that templates may require adaptation. TCPDF’s addHTMLCell() can continue content across page or region breaks; getHTMLCell() returns operators when your code needs to place them itself. The PHP manual documents libwkhtmltox as an LGPLv3 QtWebKit library. For state-of-the-art CSS or close mirroring of a live page, mPDF’s guidance points to headless Chrome.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Render a trusted template with Dompdf

Dompdf is usually the shortest path for an invoice, report or other PHP-generated document. Install it with Composer:

composer require dompdf/dompdf

A complete endpoint can look like this:

<?php
require __DIR__ . '/vendor/autoload.php';

use DompdfDompdf;

$customer = htmlspecialchars($_GET['customer'] ?? 'Example customer', ENT_QUOTES, 'UTF-8');
$html = '<!doctype html>
<html><head><meta charset="utf-8">
<style>
@page { size: A4; margin: 16mm; }
body { font-family: DejaVu Sans, sans-serif; font-size: 11pt; }
h1 { color: #17324d; }
@media print { .screen-only { display: none; } }
</style></head><body>
<h1>Statement</h1>
<p>Customer: ' . $customer . '</p>
<p>Generated: ' . date('Y-m-d') . '</p>
</body></html>';

$dompdf = new Dompdf();
$dompdf->loadHtml($html);       // trusted, sanitized HTML
$dompdf->setPaper('A4', 'portrait');
$dompdf->render();
$dompdf->stream('statement.pdf', ['Attachment' => false]);

The documented sequence is loadHtml(), optional setPaper(), render(), then stream() or output(). Set Attachment to true for a download. To store instead of send:

$pdfBytes = $dompdf->output();
file_put_contents(__DIR__ . '/storage/statement.pdf', $pdfBytes);

Use mPDF for UTF-8 documents and document features

Install mPDF and load Composer’s autoloader:

composer require mpdf/mpdf
<?php
require __DIR__ . '/vendor/autoload.php';

$mpdf = new MpdfMpdf([
    'format' => 'A4',
    'margin_left' => 16,
    'margin_right' => 16,
    'margin_top' => 16,
    'margin_bottom' => 16,
]);

$html = '<style>
@page { margin: 16mm; }
.keep-together { page-break-inside: avoid; }
</style>
<h1>Monthly report</h1>
<div class="keep-together"><h2>Summary</h2><p>Unicode: café, 東京, العربية</p></div>';

$mpdf->WriteHTML($html);
$mpdf->Output();                 // sends the PDF to the browser

Use the output destination deliberately in production. mPDF can send inline output, force a download, return a string, or write a file depending on the destination you choose. Configure a font containing every required glyph; otherwise multilingual text can become empty boxes even though the HTML is valid.

When TCPDF or a browser engine is the better fit

TCPDF/tc-lib-pdf

Choose TCPDF when page flow is part of the application’s layout logic. addHTMLCell() places HTML in a cell and continues across page or region breaks. getHTMLCell() returns drawing operators so your code can position the result precisely. This is useful for forms, labels and tightly controlled page regions, but it is less convenient than a normal document template.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

wkhtmltox

libwkhtmltox uses QtWebKit to render HTML to PDF. It can be a practical bridge for pages that exceed a PHP renderer’s CSS support, but deployment includes a native runtime, process limits, sandboxing and version management. Treat the executable as an isolated worker rather than allowing every web request to start an unrestricted process.

Headless Chrome

Use Chromium when the source page depends on JavaScript, flexbox or grid behavior, web fonts, complex print styling, or close visual parity with a page users see in a browser. The cost is operational: you must manage a browser binary, startup time, concurrency, sandbox flags, temporary profiles and crash recovery. Give jobs a timeout and a maximum output size, and recycle workers that become unhealthy.

Make the HTML print correctly

Do not simply capture your screen stylesheet. Add a print stylesheet that removes controls and defines paper geometry:

@media print {
  nav, .toolbar, .cookie-banner { display: none; }
  @page { size: A4; margin: 16mm; }
  .avoid-break { break-inside: avoid; }
}
  • Prefer normal block flow, tables and explicit widths when using Dompdf or mPDF.
  • Use page-break rules around headings, signatures, cards and table rows that must remain together.
  • Embed or configure fonts rather than relying on a visitor’s installed fonts.
  • Use absolute URLs or controlled local paths for images and stylesheets, and verify that the renderer can access them.
  • Test long tables, empty data sets, very long words, right-to-left text and the final page.

For a live browser page, first decide whether you need its current DOM after JavaScript runs or only the server’s original HTML. A PHP HTTP client retrieves source HTML; it does not automatically reproduce browser state, cookies, client-side rendering or authenticated sessions. If those states matter, a browser engine or a screenshot/PDF service is the appropriate layer.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Security and resource handling

Never feed arbitrary submitted HTML and CSS directly into a renderer. The mPDF manual explicitly warns: “mPDF is not meant to receive HMTL/CSS from an outside user.” Sanitize markup, escape interpolated values and allow only the tags, attributes, URLs and CSS properties your application needs.

  • Keep templates in application code and insert user values only after context-appropriate escaping.
  • Disable or restrict local-file access and remote URLs unless the document requires them.
  • Use an allowlist for images, fonts, stylesheets and HTTP hosts; block private-network addresses to reduce SSRF risk.
  • Apply request, render-time, memory and output-size limits.
  • Run native browser or wkhtmltox workers with a dedicated low-privilege account and an isolated temporary directory.
  • Log renderer errors without exposing HTML, credentials or personal data.

Response handling, caching and reliability

For an inline PDF, send an appropriate content type and disposition. If you use output() or a string destination, ensure no warnings, debug bars or stray whitespace are emitted before the PDF bytes. For large documents, write to a temporary file and stream it, then delete the file after the response.

Cache deterministic reports using a key containing the template version, input identity, locale, font configuration and relevant data version. Do not cache personalized PDFs under a publicly guessable URL. For asynchronous jobs, persist status and a signed download URL rather than holding an HTTP request open while a browser starts.

Troubleshooting common failures

Blank or truncated PDF

Check PHP logs for an exception, output-buffer contamination, memory exhaustion or a renderer timeout. Remove accidental output before the PDF response, increase memory only after reducing document size, and test the HTML as a standalone file.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

CSS looks wrong

Confirm that the rule is supported by the selected renderer. Replace unsupported grid, advanced positioning or generated-content tricks with block flow and tables, or move to headless Chrome. Ensure the print stylesheet is actually loaded and that media rules are intentional.

Images or web fonts are missing

Use accessible URLs or permitted local paths, verify TLS and authentication, and check file permissions. Configure a font with the needed glyphs and confirm that the renderer can read it. Avoid expiring signed URLs during a long render.

Page breaks split an important block

Wrap the block and apply break-inside: avoid (or the renderer’s equivalent), keep headings with following content, and avoid oversized elements that cannot fit on one page. Some engines can only approximate these rules.

Characters display as boxes

Declare UTF-8 in the document, use UTF-8 source files, and select a font covering the script. mPDF is designed around UTF-8 HTML; Dompdf also documents UTF-8 support, but both still require an appropriate font.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

JavaScript content is absent

Dompdf, mPDF and TCPDF are not substitutes for a full browser. Render the data server-side, wait for the page in a browser engine, or use a service that executes the page before creating the PDF.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. It can return PNG, JPEG, WebP or PDF from one request, including pages that need browser rendering. Before capture it accepts consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets; each 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 status. Its MCP server exposes take_screenshot, get_page_info and capture_pdf for Claude, Cursor and other MCP clients.

cURL:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

PHP (using cURL):

<?php
$ch = curl_init('https://api.screenshotneo.com/v1/shot?' . http_build_query([
    'access_key' => 'YOUR_API_KEY',
    'url' => 'https://stripe.com',
]));
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 90,
]);
$bytes = curl_exec($ch);
if ($bytes === false) {
    throw new RuntimeException(curl_error($ch));
}
file_put_contents(__DIR__ . '/shot.webp', $bytes);
curl_close($ch);

Python:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
r.raise_for_status()
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}`);
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
require('fs').writeFileSync('shot.webp', Buffer.from(await res.arrayBuffer()));

See the ScreenshotNeo documentation for PDF options, waits, cookies, headers, device settings and signed links. Every feature is on every plan: the free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try it.

Decision checklist

  • Choose Dompdf for a straightforward, trusted PHP template.
  • Choose mPDF for UTF-8-heavy documents and built-in document features.
  • Choose TCPDF when explicit placement and page-flow operators are central.
  • Choose wkhtmltox or headless Chrome when modern CSS or JavaScript is non-negotiable.
  • Sanitize HTML, constrain resources, configure fonts and test page breaks before production.

Frequently Asked Questions

Can PHP convert an existing URL directly to PDF?

Not reliably with a PHP-only renderer. Fetching a URL gives you source HTML, while JavaScript, cookies, authentication and browser state may change the rendered page. Use a browser engine or a service that renders the URL in a browser context when those states matter.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How do I force a PDF download instead of opening it?

With Dompdf, call stream('file.pdf', ['Attachment' => true]). For other libraries, send Content-Disposition: attachment with the filename while ensuring the PDF bytes are the only response body.

Which renderer should I use for invoices?

Dompdf or mPDF is usually sufficient for a server-controlled invoice. Select mPDF when Unicode, headers, footers or numbering are important; select a browser engine if the invoice depends on modern CSS or client-side rendering.

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.