Driver FixRecommendedSound, Wi-Fi or graphics acting up? Check drivers firstFind missing or outdated drivers fast.Check DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix Now×
Skip to content
Composer

How to Generate Multiple Pages with PHP wkhtmltopdf (URLs, Files, HTML, and Page Breaks)

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

Use one wkhtmltopdf process and add each document in the order it should appear. In PHP, install the wkhtmltopdf executable separately, install a Composer wrapper, create a Pdf object with global defaults, call addPage() for every URL, file, or HTML string, and save the resulting PDF. Page-specific options can override the global defaults.

This approach creates one PDF containing several independently rendered pages. If you instead have one long HTML document, use print CSS page-break rules. The two techniques can be combined for reports with covers, table of contents pages, and content sections.

What you need before writing PHP

  • A trusted installation of the wkhtmltopdf executable on the machine that runs PHP.
  • Composer and the mikehaertl/phpwkhtmltopdf package.
  • Permission for the PHP process to execute the binary and write the output directory.
  • Network access when pages, stylesheets, images, fonts, or scripts are remote.

The PHP package is a wrapper, not a replacement for the executable. Verify the binary first:

wkhtmltopdf --version

If the command is not on the web server’s PATH, use its absolute path in the wrapper configuration. Then install the wrapper:

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.
composer require mikehaertl/phpwkhtmltopdf

Package and binary versions, operating-system libraries, and distribution builds differ. Test the exact combination on the deployment host rather than assuming a development machine’s installation will behave identically.

Generate several URLs or files in a defined order

Each call to addPage() appends one input. The first call becomes the first document in the PDF, the second call becomes the next, and so on. Inputs may be URLs, local filenames, or HTML strings.

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

use mikehaertlwkhtmltoPdf;

$pdf = new Pdf([
    'binary' => '/usr/local/bin/wkhtmltopdf',
    'page-size' => 'A4',
    'margin-top' => '15mm',
    'margin-right' => '15mm',
    'margin-bottom' => '15mm',
    'margin-left' => '15mm',
]);

$pdf->addPage('https://example.com/page-1');
$pdf->addPage('https://example.com/page-2');
$pdf->addPage(__DIR__ . '/page-3.html', [
    'javascript-delay' => 500,
    'enable-local-file-access' => true,
]);

if (!$pdf->saveAs(__DIR__ . '/output.pdf')) {
    throw new RuntimeException($pdf->getError());
}

Global options establish defaults for every page. The array passed to addPage() changes settings only for that page. In the example, the third input gets a JavaScript delay and local-file access while the first two use the global settings.

Appending an HTML string

For generated content, pass an HTML string instead of a filename. Keep relative assets resolvable, or use absolute URLs and an appropriate base URL.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
$html = '<!doctype html>
<html><head><meta charset="utf-8"></head>
<body><h1>Invoice 1007</h1><p>Generated by PHP.</p></body></html>';

$pdf->addPage($html, [
    'page-size' => 'A4',
    'encoding' => 'UTF-8',
]);

Adding a cover or table of contents

wkhtmltopdf supports page, cover, and table-of-contents objects. Use the wrapper’s corresponding object methods when you need a cover or generated contents section, then append normal pages in their intended position. Keep the sequence explicit so a cover does not accidentally follow the report body.

Use CSS when one document must break across sheets

Multiple addPage() calls are best when each input is a separate document. For a single long HTML document, mark the elements where a new sheet should begin and protect blocks that should stay together:

<style>
.chapter { break-before: page; page-break-before: always; }
.keep-together { break-inside: avoid; page-break-inside: avoid; }
</style>

<section>Introduction</section>
<section class="chapter">Chapter 1</section>
<div class="keep-together">A table and its caption</div>

break-before and break-inside are modern print properties. The older page-break-* declarations provide a useful fallback for wkhtmltopdf’s older WebKit engine. They are requests, not guarantees: oversized elements, floats, tables, and WebKit layout rules can still produce unexpected splits. Always inspect the rendered PDF.

Choosing separate pages or CSS breaks

Requirement Recommended method Reason
Several independent URLs One addPage() call per URL Order is explicit and each page can have its own options.
Local HTML files with shared styling One call per file, or combine into one document Separate calls isolate failures; one document gives CSS control across sections.
One long report One HTML document plus print CSS Sections, tables, and headings can be laid out together.
Cover or contents section wkhtmltopdf cover/TOC object followed by page objects Special objects occupy a defined position in the PDF.

Control headers, footers, and paper layout

Set paper size and margins globally, then override unusual pages. Header and footer strings can include substitutions such as [page], [topage], [webpage], [date], and [isodate]. For example:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
$pdf = new Pdf([
    'page-size' => 'A4',
    'margin-top' => '22mm',
    'header-right' => 'Page [page] of [topage]',
    'footer-center' => '[isodate]',
]);

Reserve enough top and bottom margin for the header or footer. If you need logos, multiple styles, or richer markup, use an HTML header/footer file and reference it with the relevant wkhtmltopdf option. A header that overlaps body content is usually a margin problem, not a CSS page-break problem.

Make JavaScript and local assets render reliably

JavaScript-generated content

wkhtmltopdf can capture a page before a client-side application finishes. Give it time with javascript-delay, or configure the documented window-status mechanism when your page can signal that rendering is complete. A delay is simple but adds the same wait to every run; a status signal can finish sooner when content is ready.

$pdf->addPage('https://example.com/dashboard', [
    'javascript-delay' => 1500,
]);

Do not treat a successful process exit as proof that asynchronous content appeared. Check the PDF for charts, images, and data populated after the initial HTML response.

Local CSS, images, and fonts

Local-file access is restricted in many builds. Enable it for a trusted directory, or allow only the directory that contains the assets. Limiting access is safer than exposing the entire filesystem.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
$pdf->addPage(__DIR__ . '/report.html', [
    'enable-local-file-access' => true,
    // Prefer an explicit allow-list when your build supports it:
    // 'allow' => __DIR__ . '/public-assets',
]);

Use predictable, readable paths and verify that the PHP user can read every referenced file. A missing font or image may appear as a blank area while the command still returns a PDF.

Remote authentication and resources

Authenticated pages may require cookies, custom headers, or an application-specific session. Configure those options deliberately and avoid placing secrets in URLs or logs. If a page depends on third-party resources, a blocked request, certificate problem, or rate limit can change the output without changing your PHP code.

Handle failures instead of returning a corrupt PDF

Production code should check the wrapper result and retain the executable’s diagnostic output. The wrapper exposes an error message through getError(); execution failures should be caught and logged with the input identifier and command context.

try {
    $pdf = new Pdf([
        'binary' => '/usr/local/bin/wkhtmltopdf',
        'page-size' => 'A4',
    ]);

    foreach ($urls as $url) {
        $pdf->addPage($url);
    }

    if (!$pdf->saveAs($destination)) {
        throw new RuntimeException($pdf->getError());
    }
} catch (Throwable $e) {
    error_log('PDF generation failed: ' . $e->getMessage());
    throw $e;
}

Write to a temporary filename and move it into place only after a successful save. This prevents readers from downloading a partially written file when a worker times out.

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

Troubleshooting common problems

“The binary could not be found”

Cause: PHP’s process environment has a different PATH from your shell, or wkhtmltopdf is not installed.

Fix: Run wkhtmltopdf --version as the deployment user and set 'binary' to the full executable path.

Local images or styles are missing

Cause: local-file access is disabled, paths are relative to a different working directory, or the PHP user lacks read permission.

Fix: use an absolute HTML path, enable local access for trusted content, or add a narrow allow-list; then verify permissions and asset paths.

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

Charts or data are blank

Cause: JavaScript had not finished when rendering began.

Fix: add a measured javascript-delay or use a completion status signal. Confirm that the data endpoint is reachable from the server.

Pages break in the wrong place

Cause: an element is taller than the remaining space, table layout differs from a modern browser, or only modern break properties were supplied.

Fix: add both modern and legacy break rules, use break-inside: avoid on suitable blocks, simplify oversized elements, and inspect the actual PDF.

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

The PDF is empty or a URL shows an error page

Cause: DNS, TLS, authentication, redirects, bot protection, or a server-side timeout.

Fix: fetch the URL from the same host, test authentication separately, review stderr, and increase waits only after fixing connectivity or access problems.

It works locally but fails in production

Cause: different binary builds, missing shared libraries, sandbox permissions, fonts, or environment variables.

Fix: record the binary version, install required system dependencies, deploy fonts and assets, and run a smoke-test PDF during release.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance, reliability, and cost considerations

Every input page is rendered by the executable, so total time grows with page count, page complexity, network latency, and JavaScript waits. Reuse a stable configuration, avoid unnecessary delays, and keep assets close to the rendering host. For large batches, queue jobs and limit concurrency so CPU, memory, and file descriptors remain available.

Cache or pre-render static pages when freshness allows. Do not cache personalized pages without a clear isolation policy. Set application-level timeouts longer than the expected render time, but still enforce an upper bound so one unavailable URL cannot occupy a worker indefinitely.

Measure your own documents; the available implementation guidance does not establish a universal benchmark. Log input order, binary version, elapsed time, output size, and stderr so a changed package or page can be diagnosed.

Or skip the browser setup

If you need screenshots rather than a multi-document PDF, ScreenshotNeo provides a single website screenshot API call. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. It also offers an MCP server for Claude, Cursor, and other MCP clients, with take_screenshot, get_page_info, and capture_pdf tools.

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

For the full parameter list, see the ScreenshotNeo documentation. A direct call looks like this:

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

ScreenshotNeo has a free tier of 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. If that fits your use case, sign up for the free plan.

Equivalent calls from Python and Node.js

These examples use the same ScreenshotNeo endpoint and return the image bytes. Change the target URL and output filename as needed.

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(`Screenshot failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

Frequently Asked Questions

Can I mix URLs, local files, and HTML strings in one PDF?

Yes. Call addPage() for each input in the required sequence; the wrapper accepts those input types and allows per-page options.

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

Why does a successful command still produce the wrong layout?

wkhtmltopdf uses an older WebKit rendering engine. CSS support and pagination can differ from a current browser, so inspect the PDF and provide legacy page-break fallbacks.

Should I use one huge HTML document or many addPage() calls?

Use separate calls for independent documents or per-page settings; use one document when shared CSS and cross-section layout control matter most.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

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

Read next

Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.