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

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

The error means mPDF’s PCRE parser reached PHP’s pcre.backtrack_limit while processing an HTML or CSS string. The safest fix is to split the document into smaller, structurally valid chunks and pass them to WriteHTML() separately. If you control PHP configuration, you can also raise the limit to a bounded value, then retest memory use and stability. Large tables, expensive CSS, and incompatible PHP/mPDF versions may still need separate fixes.

What the error means

mPDF converts HTML and CSS into a PDF by parsing the strings supplied to it. During that parsing, PHP’s PCRE engine may need to backtrack through regular-expression patterns. When the work reaches pcre.backtrack_limit, mPDF stops and reports an exception such as:

The HTML code size is larger than pcre.backtrack_limit 1000000. You should use WriteHTML() with smaller string lengths.

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

PHP’s current documentation lists a default backtrack limit of 1,000,000. Older PHP versions before 5.3.7 used 100,000. The setting is marked INI_ALL, but whether a runtime change is accepted still depends on your hosting policy and PHP build. A higher number is not automatically safer: PHP warns that extreme values can consume process stack space and crash the process.

Fix it in the right order

  1. Confirm which WriteHTML() call fails and record the approximate byte length of the HTML string.
  2. Split the input at safe structural boundaries and call WriteHTML() repeatedly.
  3. If you cannot split the content enough, raise the limit modestly in the permitted PHP configuration layer and retest under realistic load.
  4. Reduce table and CSS complexity if the document remains slow, memory-hungry, or unstable.
  5. Check that your PHP version is supported by the installed mPDF release.

Primary fix: chunk the HTML passed to WriteHTML()

Chunking is the documented mPDF remedy because each regular-expression operation sees a smaller string. Split between records, sections, or complete table groups—not in the middle of a tag, attribute, table row, or CSS rule.

A reusable chunking pattern

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

$mpdf = new MpdfMpdf();

$css = '<style>
  body { font-family: sans-serif; font-size: 10pt; }
  table { border-collapse: collapse; width: 100%; }
  th, td { border: 0.2mm solid #999; padding: 1.5mm; }
</style>';

$mpdf->WriteHTML($css, MpdfOutputDestination::INLINE);

$mpdf->WriteHTML('<h1>Sales report</h1><table><thead>
  <tr><th>ID</th><th>Customer</th><th>Amount</th></tr>
</thead><tbody>');

foreach (array_chunk($rows, 250) as $group) {
    $html = '';
    foreach ($group as $row) {
        $id = htmlspecialchars((string) $row['id'], ENT_QUOTES, 'UTF-8');
        $customer = htmlspecialchars((string) $row['customer'], ENT_QUOTES, 'UTF-8');
        $amount = htmlspecialchars((string) $row['amount'], ENT_QUOTES, 'UTF-8');
        $html .= "<tr><td>{$id}</td><td>{$customer}</td><td>{$amount}</td></tr>";
    }
    $mpdf->WriteHTML($html);
}

$mpdf->WriteHTML('</tbody></table>');
$mpdf->Output('report.pdf', MpdfOutputDestination::DOWNLOAD);

The opening and closing table markup are deliberately separate, while every chunk contains complete rows. Keep shared CSS in one small call. If a section must start on a new page, issue $mpdf->AddPage() between chunks or include a page-break rule at a section boundary.

Choose a chunk size empirically

There is no universal safe row count. A 250-row group may work for simple records but fail for cells containing long text, nested tables, images, or complex inline styles. Start conservatively, log the size of each string, and reduce the group when a call still fails. A useful diagnostic is:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
$html = buildRows($group);
error_log('mPDF chunk bytes: ' . strlen($html));
$mpdf->WriteHTML($html);

Use strlen() for bytes, not a visual character count. Keep each chunk valid HTML in the context mPDF expects, and avoid repeatedly sending a huge stylesheet with every group.

Raise pcre.backtrack_limit cautiously

If the document cannot be split at a useful boundary, try a bounded increase. The following must run before the failing WriteHTML() call and only works when the host permits runtime changes:

<?php
$old = ini_get('pcre.backtrack_limit');
if (!ini_set('pcre.backtrack_limit', '2000000')) {
    throw new RuntimeException('The host rejected the runtime PCRE limit change.');
}

// Generate the PDF and monitor memory and worker stability.
$mpdf->WriteHTML($html);

error_log('pcre.backtrack_limit was ' . $old . '; now ' . ini_get('pcre.backtrack_limit'));

2,000,000 is an example of a bounded test value, not a universal recommendation. Select a value for your PHP runtime and workload, then load-test the largest expected document. Do not jump to an extreme number simply to suppress the exception: the PHP manual warns of stack consumption and crashes. If ini_set() returns false, configure the setting in the PHP layer your deployment controls (for example, the applicable server configuration) or use chunking instead.

Large tables and CSS can remain the real bottleneck

Reduce table cost

  • Render fewer columns or split one very wide report into logical sections.
  • Use simpler borders and padding where the design allows.
  • Consider mPDF’s simpleTables option only when you do not need complex borders and padding. It can reduce processing work, but it changes layout fidelity.
  • Process long reports in groups and avoid building one giant PHP string before rendering.

mPDF’s performance guidance identifies large tables as a major cost. Chunking lowers peak parser work, but it cannot make intrinsically expensive table layout free.

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

Simplify CSS and document structure

  • Remove unused selectors and deeply nested rules.
  • Avoid unsupported or expensive layout features when a basic table or block layout is sufficient.
  • Keep CSS declarations consistent across chunks; send common rules once.
  • Validate tags, quoting, and entity encoding before blaming the numeric limit.

If increasing the setting changes the message to a regex compilation error, inspect malformed or overly complex CSS and HTML. A higher backtrack limit cannot repair invalid input or a compilation failure.

Memory, timeout, and reliability checks

Long documents can hit several limits at once. Check PHP’s memory_limit and execution timeout, web-server or queue-worker timeouts, and available process memory. mPDF’s memory guidance recommends processing very long documents in small chunks. Keep enough headroom for images, fonts, table layout, and the PDF buffer; a successful test on a small report does not prove the largest report is safe.

  • Run a production-sized fixture, including the longest text and largest tables.
  • Record peak memory, elapsed time, chunk byte sizes, and the first failing chunk.
  • Generate large PDFs in a queue worker when a web request timeout is likely.
  • Retry only failures that are safe to retry; do not duplicate a paid or externally visible job without an idempotency plan.

Check PHP and mPDF compatibility

Install mPDF through Composer and compare your PHP version with the support table in the project’s official repository. An old mPDF release on a newer PHP runtime, or the reverse, can produce behavior that looks like an input-size problem. Upgrade in a staging environment, regenerate representative PDFs, and review any changed CSS or font behavior before production rollout.

composer require mpdf/mpdf
composer show mpdf/mpdf
php -v

Keep the lock file committed so production and development use the same mPDF version. Do not treat an upgrade as a substitute for chunking when the input is genuinely oversized.

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

Chunking versus raising the limit

Remedy Best fit Stability risk Implementation effort Main caveat
Chunk WriteHTML() input Any deployment, especially shared hosting Usually lower peak parser pressure Moderate: requires safe boundaries Requires careful handling of tables, CSS, and page breaks
Raise pcre.backtrack_limit Servers where PHP configuration is controlled Higher if set excessively; stack or memory failure is possible Low code effort, higher testing burden No universal safe value; host may reject runtime changes
Simplify tables/CSS Reports with expensive layout Can improve memory and speed Moderate design work May reduce visual fidelity
Upgrade mPDF/PHP pairing Unsupported or outdated installations Requires regression testing Moderate Version changes may affect rendering

Troubleshooting by symptom

The same exception appears after splitting

Reduce the chunk size, log byte lengths, and verify that a supposedly small chunk is not carrying duplicated CSS, a huge inline image, or an accidentally concatenated previous chunk.

ini_set() has no effect

The host may disallow the change or apply a different PHP configuration to the worker running mPDF. Check ini_get() in that exact process and move the setting to the permitted server configuration, or rely on chunking.

The PDF is incomplete or has broken tables

Do not split inside a row or tag. Open the table once, send complete <tr> groups, then close </tbody> and </table> in a final call. Ensure exceptions are not being swallowed between chunks.

Memory exhaustion follows the limit increase

Revert the increase, lower chunk sizes, simplify layout, and inspect image and font usage. A larger PCRE allowance can let one call consume more process memory before failing.

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

A regex compilation error replaces the size error

Inspect CSS and HTML validity and remove unusually complex patterns or malformed declarations. This indicates a different parser problem, not merely an insufficient numeric limit.

Only very large tables fail

Apply table-specific changes: smaller groups, simpler borders, fewer columns, or simpleTables when its fidelity trade-off is acceptable. Test with the largest expected row count.

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

Or skip the browser setup

If your workflow also needs clean website screenshots for documentation or report attachments, ScreenshotNeo provides a one-call API. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf.

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

See the ScreenshotNeo API documentation for all options. The service includes full-page and element capture, device and viewport settings, retina scale, PDF controls, custom CSS and JavaScript, click and wait actions, request blocking, headers, cookies, user-agent, authorization, timezone, geolocation, transparent backgrounds, resizing, TTL caching, signed links, async webhooks, bulk capture of up to 100 URLs per call, usage information, and an OpenAPI specification. Every feature is available on every plan. The Free plan includes 1,000 screenshots each month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

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

FAQ

Is this an mPDF license error?

No. The message describes a PHP PCRE processing limit reached while mPDF parses input. Licensing does not change the parser limit.

Can I set the limit once for every project?

You can set it in a shared PHP configuration layer when you control that environment, but the appropriate value depends on workload. Validate the setting in each runtime and keep chunking available for unusually large documents.

Does converting HTML to a string with fewer characters always solve it?

No. Byte size, CSS complexity, table layout, inline assets, and parser structure all matter. A shorter-looking document can still require substantial backtracking.

Frequently Asked Questions

Is this an mPDF license error?

No. It is a PHP PCRE processing limit reached while mPDF parses the supplied HTML or CSS.

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

Can I set the limit once for every project?

Only if you control the relevant PHP configuration. Choose and test a bounded value for each workload, and retain chunking for unusually large documents.

Does fewer visible characters always solve the problem?

No. Byte size, CSS complexity, table layout, inline assets, and parser structure also affect processing.

The Bottom Line

Split oversized HTML at safe boundaries first. Raise pcre.backtrack_limit only when you control the runtime and can test memory and stability; then simplify expensive tables and verify PHP/mPDF compatibility.

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.

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.