PHP cURL does not render a web page into a PDF by itself. It sends a URL or HTML document to a rendering engine, receives PDF bytes, and then saves or streams those bytes. For a reliable full-page result, choose a renderer (a hosted HTML-to-PDF API, local wkhtmltopdf, or headless Chrome), wait for the page’s assets and JavaScript, set print options explicitly, and validate the binary response before writing it to disk.
Choose the rendering path first
Your choice affects CSS and JavaScript fidelity, access to private pages, deployment work, and recurring cost.
| Approach | How PHP is involved | Best fit | Main trade-off |
|---|---|---|---|
| Hosted HTML-to-PDF API | PHP cURL posts a URL or HTML and receives a PDF | Production apps that prefer managed browsers | Authentication, network dependency and provider fees |
| Local wkhtmltopdf | PHP starts the installed command-line process | Self-hosted, predictable command-line workflows | Older Qt WebKit rendering and process packaging |
| Headless Chromium | PHP invokes Chrome directly or uses a PHP client/managed service | Modern CSS and JavaScript-heavy pages | Browser installation, sandbox and lifecycle management |
There is no independently comparable speed or fidelity benchmark across these options in the published documentation, so test your own pages, fonts, charts and long tables.
Before you write code
- Public versus private input: a renderer must be able to reach every URL, image, font and script. For private content, send generated HTML or use documented authentication headers, cookies or a renderer that can log in.
- Absolute assets: relative paths such as
/css/app.cssneed a correct page origin or a providerbaseUrl. Cross-origin resources must also be reachable from the renderer. - Readiness: a
loadevent can occur before client-rendered content, lazy images or web fonts are ready. Use a bounded delay or a documentednetworkidle0/networkidle2condition. - Print styling: define paper size, orientation, margins and background printing. Add
@page, page-break rules andprint-color-adjustwhere your design requires them. - Output handling: a PDF is binary. Never concatenate debug text, PHP notices or an HTML error page into the file.
Method 1: request a PDF from a hosted API with PHP cURL
HTML PDF API documents a POST request to https://htmlpdfapi.com/api/v1/pdf. Supply exactly one input: url, file or html. The example below sends a public URL, enables backgrounds and sets a 1280×900 viewport.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →#1 Best Overall
Complete PHP example
<?php
$token = getenv('HTMLPDFAPI_TOKEN');
$pageUrl = 'https://example.com/report';
$output = __DIR__ . '/report.pdf';
$ch = curl_init('https://htmlpdfapi.com/api/v1/pdf');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authentication: Token ' . $token,
'Content-Type: application/x-www-form-urlencoded',
],
CURLOPT_POSTFIELDS => http_build_query([
'url' => $pageUrl,
'background' => 'true',
'viewport_size' => '1280x900',
]),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 60,
]);
$pdf = curl_exec($ch);
$curlError = curl_error($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
curl_close($ch);
if ($pdf === false) {
throw new RuntimeException('Transport error: ' . $curlError);
}
if ($status >= 400) {
throw new RuntimeException("PDF service returned HTTP $status");
}
if (stripos((string) $contentType, 'application/pdf') === false || strlen($pdf) === 0) {
throw new RuntimeException('Response was not a non-empty PDF');
}
if (file_put_contents($output, $pdf) === false) {
throw new RuntimeException('Could not write ' . $output);
}
echo "Saved $outputn";
The vendor’s documented cURL pattern also posts a URL and redirects the binary response to result.pdf. Keep the authentication header and field names exactly as specified by the provider’s current documentation; do not assume another API accepts the same names.
Posting generated HTML instead
For a private page or a report assembled in PHP, replace url with the provider’s documented html field (or upload a file). Inline critical CSS or provide a valid base URL for relative assets. Use the service’s controls for links, viewport, print media, headers, footers, spacing and page numbering when you need them.
Streaming the result to a browser
<?php
// After the same validation shown above:
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="report.pdf"');
header('Content-Length: ' . strlen($pdf));
echo $pdf;
exit;
Send these headers before any output. For a forced download, change inline to attachment.
Method 2: run wkhtmltopdf locally
wkhtmltopdf is an open-source (LGPLv3) command-line tool that renders HTML to PDF with Qt WebKit and runs headlessly without a display service. Its basic form is wkhtmltopdf http://google.com google.pdf.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Invoke it safely from PHP
<?php
$url = 'https://example.com/report';
$output = __DIR__ . '/report.pdf';
$binary = '/usr/local/bin/wkhtmltopdf'; // Pin the path you installed and verified.
$command = sprintf(
'%s %s %s',
escapeshellarg($binary),
escapeshellarg($url),
escapeshellarg($output)
);
$descriptor = [
0 => ['pipe', 'r'],
1 => ['pipe', 'w'],
2 => ['pipe', 'w'],
];
$process = proc_open($command, $descriptor, $pipes);
if (!is_resource($process)) {
throw new RuntimeException('Could not start wkhtmltopdf');
}
fclose($pipes[0]);
$stdout = stream_get_contents($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
$exitCode = proc_close($process);
if ($exitCode !== 0 || !is_file($output) || filesize($output) === 0) {
throw new RuntimeException("wkhtmltopdf failed ($exitCode): $stderr");
}
echo "Created $outputn";
Use an absolute binary path, capture standard error, and inspect the exit code. A maintained PHP wrapper can expose page size, orientation, margins, headers and footers; configure its binary option explicitly. Some wrapper features require an X server, which is unavailable on many headless servers, so verify your deployment rather than assuming every option works.
Rank #2
Typical print options
Add the renderer’s documented switches for paper size, landscape orientation, margins, JavaScript delay and background graphics. Keep untrusted URLs and shell arguments escaped; never interpolate user input into an unescaped command.
Method 3: print with headless Chrome
Chrome’s command line can print a URL directly:
chrome --headless --print-to-pdf=report.pdf --no-pdf-header-footer --timeout=30000 https://developer.chrome.com/
--no-pdf-header-footer removes browser-generated date, URL and page-number decorations. Increase --timeout for pages that need more time to render, but keep a finite limit.
Using a PHP Chrome client
The chrome-php library supports navigation, waitForNavigation(), setHtml(), PDF options, and saveToFile()/saveToStream(). Its PDF settings include printBackground, paper dimensions, margins, scale and header/footer templates. A managed PHP client such as ChromeHeadless.io accepts html or url, readiness values including load, domcontentloaded, networkidle0 and networkidle2, plus format, orientation, margins, page ranges, backgrounds and templates. Follow the selected library’s installation and current method signatures; those differ between releases.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsMake a capture genuinely full-page
- Wait for content: use
networkidle0ornetworkidle2where supported, or a bounded delay. Also wait for a known selector when your application exposes one. - Load lazy content: scroll or use the renderer’s full-page/lazy-image option so below-the-fold images are requested before printing.
- Enable backgrounds: set the API or browser option that prints CSS backgrounds and images.
- Set geometry: choose paper format, portrait or landscape, margins and scale. Wide dashboards often need landscape or a smaller scale.
- Control pagination: use
@page,break-before,break-afterandbreak-inside. Avoid placing a sticky header or fixed overlay over every page unless that is intentional. - Check fonts and assets: wait for web fonts, use stable absolute URLs, and ensure the renderer can access authenticated resources.
- Inspect the result: verify HTTP status, content type and nonzero length, then open the PDF and check the final page, tables, links and images.
Example print CSS
@page { size: A4; margin: 14mm; }
@media print {
.screen-only { display: none !important; }
thead { display: table-header-group; }
tr, img, figure { break-inside: avoid; }
* { print-color-adjust: exact; -webkit-print-color-adjust: exact; }
}
Or skip the browser setup
ScreenshotNeo is a hosted website screenshot API and MCP server. It can return a PDF from one GET request while handling browser setup for you:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
For a PDF response, request the PDF output according to the ScreenshotNeo API documentation. The service can load lazy images, wait for a selector, delay or network idle, set viewport and device options, run custom JavaScript, click elements, hide selectors, supply cookies or headers, set timezone and geolocation, and capture full pages. It also supports PDF paper size, margins, landscape mode and page ranges.
- Cookie and consent banners, newsletter popups and chat widgets are removed before the shot; 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 status.
- An MCP server provides
take_screenshot,get_page_infoandcapture_pdftools for Claude, Cursor and other MCP clients. - The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Every feature is available on every plan.
Create a free ScreenshotNeo account to try it without a card.
Troubleshooting
The file is HTML, empty or unreadable
Log the HTTP status and Content-Type. A 401/403 usually means an invalid token or inaccessible page; a 200 response with text/html may be an API error page. Do not save until the response is a non-empty PDF.
Styles, images or fonts are missing
Use absolute asset URLs, confirm DNS/TLS access from the renderer, provide cookies or authorization where documented, and wait for fonts and network activity. For generated HTML, set the correct base URL.
JavaScript content is absent
Capture after a selector appears or after networkidle0/networkidle2; a plain load event can be too early. Increase a bounded timeout only after confirming the page eventually settles.
Background colors disappear
Enable background printing in the API, Chrome client or wkhtmltopdf options, and include print-color-adjust: exact in print CSS.
Rank #4
Pages are clipped or have large blank areas
Set paper size, orientation, margins and scale explicitly. Check for fixed-width containers, oversized images and CSS that only targets screen media.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →wkhtmltopdf fails on a server
Verify the absolute binary path and executable permissions, inspect stderr and exit code, and check whether the selected wrapper option requires an X server. Move to headless Chrome or a managed renderer if the binary cannot run in your environment.
The PHP request times out
Use a realistic cURL timeout, reduce unnecessary page work, and use an asynchronous job/webhook workflow when the provider offers one. Do not remove timeouts entirely; a permanently loading page should fail predictably.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Performance, reliability and cost decisions
- Local engines: avoid per-request vendor charges, but you own browser binaries, fonts, sandboxing, upgrades, concurrency and crash recovery.
- Managed APIs: reduce operations work and commonly expose readiness, authentication and PDF controls, but add network latency, service authentication and recurring usage cost.
- Caching: cache only when the page can be stale; use a TTL and invalidate it when source data changes. A cached response must still be validated before serving.
- Concurrency: limit simultaneous browser processes, set memory/time budgets and queue large jobs. Record renderer version, URL, options, status and output size for diagnosis.
- Security: restrict outbound destinations if users can submit URLs, protect API tokens in environment variables, and avoid logging sensitive HTML, cookies or authorization headers.
FAQ
Can PHP cURL convert HTML to PDF without another program?
No. cURL transports bytes; a PDF renderer must interpret HTML, CSS and JavaScript.
Should I send a URL or HTML?
Send a URL for a public page that the renderer can access. Send HTML when content is private or generated dynamically, while supplying a base URL or absolute assets.
How do I prevent browser-added headers and footers?
With headless Chrome, use --no-pdf-header-footer; with an API or library, disable its header/footer templates and configure margins.
Is wkhtmltopdf equivalent to Chrome?
No. wkhtmltopdf uses Qt WebKit, while Chrome uses a modern Chromium engine. CSS and JavaScript behavior can differ, so test the exact documents you publish.
Frequently Asked Questions
Can PHP cURL convert HTML to PDF without another program?
No. cURL transports bytes; a PDF renderer must interpret HTML, CSS and JavaScript.
Should I send a URL or HTML?
Send a URL for a public page that the renderer can access. Send HTML when content is private or generated dynamically, while supplying a base URL or absolute assets.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchHow do I prevent browser-added headers and footers?
With headless Chrome, use –no-pdf-header-footer; with an API or library, disable its header/footer templates and configure margins.
Is wkhtmltopdf equivalent to Chrome?
No. wkhtmltopdf uses Qt WebKit, while Chrome uses a modern Chromium engine. CSS and JavaScript behavior can differ, so test the exact documents you publish.
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.

