Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →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 the resulting PDF bytes, and then saves or streams those bytes. For a reliable full-page document, choose a renderer (a hosted HTML-to-PDF API, wkhtmltopdf, or headless Chrome), wait for the page to be ready, enable backgrounds, set print dimensions, and validate the binary response before writing it to disk.
The rendering pipeline
A complete conversion has three separate jobs:
- Input: a public URL, authenticated HTML, or generated HTML/CSS.
- Rendering: a browser engine lays out CSS, runs JavaScript, loads images and fonts, and paginates the result.
- Transport and storage: PHP cURL sends the request; PHP checks the HTTP response and writes the binary PDF or streams it to a browser.
If you call a URL with ordinary cURL and save the response, you will normally save HTML, not a PDF. The renderer must run either on your server or on a service that exposes an HTTP endpoint.
Choose a renderer before writing PHP
| Approach | Best fit | Important controls | Trade-offs |
|---|---|---|---|
| Hosted HTML-to-PDF API | Production applications that do not want to package a browser | URL, HTML or file input; viewport; print media; backgrounds; headers, footers and page numbers; margins and links | Requires authentication, network access and a provider fee; exact field names differ by vendor |
| wkhtmltopdf | Self-hosted jobs where a lightweight, scriptable binary is acceptable | URL, output path, page size, orientation, margins, headers and footers | Uses the Qt WebKit engine; you own installation, upgrades, fonts and process isolation |
| Headless Chromium | Pages that depend on modern JavaScript and browser CSS | Headless printing, timeout, print backgrounds, paper dimensions, margins, scale, page ranges and templates | Browser packaging and version management consume more resources than a simple HTTP request |
There is no independently comparable speed or fidelity benchmark across these choices. Test the engine against your own pages, especially long tables, web fonts, lazy images and client-rendered content.
Hosted HTML-to-PDF API with PHP cURL
A hosted service is the shortest deployment path. HTML PDF API documents POST https://htmlpdfapi.com/api/v1/pdf, an authentication header such as Authentication: Token <token>, and exactly one input field: url, file or html. The successful response is the PDF itself. Its documented controls include background printing, internal and external links, viewport size, print media type, headers, footers, spacing and page numbering.
#1 Best Overall
Save a URL as page.pdf
<?php
$token = 'YOUR_TOKEN';
$pageUrl = 'https://example.com/report';
$output = __DIR__ . '/page.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);
$error = curl_error($ch);
$status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$type = (string) curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
curl_close($ch);
if ($pdf === false) {
throw new RuntimeException('cURL failed: ' . $error);
}
if ($status >= 400) {
throw new RuntimeException('PDF service returned HTTP ' . $status);
}
if (stripos($type, 'application/pdf') === false || strlen($pdf) === 0) {
throw new RuntimeException('The response was not a non-empty PDF');
}
if (file_put_contents($output, $pdf) === false) {
throw new RuntimeException('Could not write ' . $output);
}
echo 'Wrote ' . $output . PHP_EOL;
Use the provider’s current documentation for authentication and field names. If you send HTML instead of a URL, include a usable base URL or absolute asset URLs so stylesheets, images and fonts can be fetched. A private page generally requires sending the generated HTML, supplying documented authentication headers/cookies, or using a renderer that can reach the private network.
Run wkhtmltopdf locally from PHP
wkhtmltopdf is an open-source (LGPLv3) command-line tool that renders HTML to PDF and images with Qt WebKit and runs headlessly without a display service. Install a pinned, trusted build, record its absolute path, and verify it during deployment.
Minimal command
wkhtmltopdf https://example.com/report report.pdf
Invoke it safely and inspect failures
<?php
$binary = '/usr/local/bin/wkhtmltopdf';
$pageUrl = 'https://example.com/report';
$output = __DIR__ . '/report.pdf';
$command = implode(' ', [
escapeshellarg($binary),
'--page-size', 'A4',
escapeshellarg($pageUrl),
escapeshellarg($output),
]);
$spec = [
0 => ['pipe', 'r'],
1 => ['pipe', 'w'],
2 => ['pipe', 'w'],
];
$process = proc_open($command, $spec, $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 . '): ' . trim($stderr));
}
echo 'Created ' . $output . PHP_EOL;
For a maintained PHP wrapper, configure its binary option and set page size, orientation, margins, headers and footers through the wrapper’s documented methods. Some wrapper features require an X server; that is a problem on many headless servers, so test the exact build in the same environment used for production. Never concatenate untrusted URLs or options into a shell command without escaping them.
Use headless Chrome for modern pages
Chrome’s command-line documentation provides this direct print operation:
Free tools Windows power users keep installed
One-click scans. No signup required.
Rank #2
chrome --headless --print-to-pdf https://developer.chrome.com/
Add --no-pdf-header-footer when browser-generated date, URL and page-number decorations are unwanted. Use --timeout for pages that need additional loading time. A PHP process can run the same command and check its exit status and output file, just as with wkhtmltopdf:
<?php
$chrome = '/usr/bin/google-chrome';
$url = 'https://example.com/report';
$output = __DIR__ . '/chrome-report.pdf';
$command = implode(' ', [
escapeshellarg($chrome),
'--headless',
'--no-sandbox',
'--disable-gpu',
'--no-pdf-header-footer',
'--timeout=15000',
'--print-to-pdf=' . escapeshellarg($output),
escapeshellarg($url),
]);
$code = 0;
exec($command . ' 2>&1', $log, $code);
if ($code !== 0 || !is_file($output) || filesize($output) === 0) {
throw new RuntimeException('Chrome PDF failed: ' . implode(PHP_EOL, $log));
}
The chrome-php library exposes navigation, waitForNavigation(), setHtml(), PDF options and saveToFile()/saveToStream(). Its documented options include printBackground, paper dimensions, margins, scale and header/footer templates. ChromeHeadless.io’s PHP client accepts either html or url, readiness values such as load, domcontentloaded, networkidle0 and networkidle2, and PDF format, orientation, margins, page ranges, backgrounds and templates.
Make a full-page capture reliable
1. Choose the right input
Public URLs are simplest. For a private page, send the rendered HTML, configure the provider’s documented cookies or headers, or run the browser inside the network that can reach the page. A renderer cannot capture a page it cannot authenticate to or resolve.
2. Make every asset reachable
Use absolute URLs for CSS, images and web fonts, or configure the renderer’s documented baseUrl/host setting. Check that HTTPS certificates, DNS and robots or firewall rules do not block the rendering environment.
3. Wait for content, not merely the first response
Client-rendered applications can be empty at the initial load event. Prefer networkidle0 or networkidle2 when available, or wait for a page-specific selector with a bounded timeout. A long-polling connection may never become idle; in that case wait for a selector or use a fixed, measured delay.
4. Set paper and print behavior explicitly
Specify paper format, orientation, margins, scale and print media type. Enable background graphics when colored sections or images are part of the design. Otherwise a renderer’s defaults can clip wide content, omit colors or create unexpected whitespace.
5. Add print CSS
@page {
size: A4 portrait;
margin: 12mm;
}
@media print {
* {
print-color-adjust: exact;
-webkit-print-color-adjust: exact;
}
.avoid-break {
break-inside: avoid;
}
.new-page {
break-before: page;
}
header.fixed, nav.sticky {
position: static;
}
}
Test long tables, sticky headers, lazy-loaded images and web fonts. Lazy images may need to be scrolled into view or explicitly loaded before printing. Fixed-position navigation that looks correct on screen can overlap every PDF page unless print CSS changes it.
6. Treat the response as binary data
Check cURL’s error string, HTTP status, content type and a nonzero byte count before saving. A service may return an HTML or JSON error document with HTTP 200 in an unusual configuration; checking the PDF signature (the first bytes are normally %PDF-) provides another useful guard. Do not emit PHP notices, whitespace or a UTF-8 byte-order mark before sending a streamed PDF.
Stream the PDF to a browser instead of saving it
Once $pdf has passed the checks above, send it with PDF headers:
Rank #4
<?php
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="report.pdf"');
header('Content-Length: ' . strlen($pdf));
echo $pdf;
Use attachment instead of inline to force a download. Keep all logging outside the response body; write diagnostics to a server log.
Options that affect pagination and fidelity
- Backgrounds: turn them on when the layout depends on fills or images.
- Viewport: set a width that matches the design’s breakpoints; a narrow default can trigger mobile CSS.
- Media type: choose print media when a print stylesheet is intended, or screen media when the screen layout is the required output.
- Margins and scale: reserve space for headers and footers and reduce scale only when content is clipped.
- Headers and footers: use renderer templates or CSS, but not both for the same element.
- Page ranges: render selected pages for previews or retries when the renderer supports ranges.
- Links: enable internal and external link handling when the PDF is a navigable document.
Troubleshooting common failures
| Symptom | Likely cause | Fix |
|---|---|---|
| The saved file opens as an HTML error page | Authentication failed, the URL was rejected, or the service returned an error body | Log HTTP status and content type, inspect the response only in a secure log, and verify the token, input field and URL. |
| PDF is blank or missing application content | Capture happened before JavaScript finished | Wait for a selector, networkidle0/networkidle2, or a bounded delay; confirm API calls succeed from the renderer. |
| CSS, images or fonts are absent | Relative URLs, blocked resources or private asset hosts | Use absolute URLs or the documented base URL, allow the renderer’s IP, and check certificate and DNS errors. |
| Colors disappear | Background printing is disabled or print CSS overrides colors | Enable background graphics and use print-color-adjust: exact where supported. |
| Right side of a page is clipped | Viewport, paper width, margins or scale do not match the layout | Set the viewport and paper explicitly, use landscape for genuinely wide content, then adjust margins or scale. |
| Every page contains an overlapping menu | Screen-only fixed or sticky elements remain active in print | Override them in @media print and test page breaks. |
| PHP reports a timeout | Slow JavaScript, fonts, images or a renderer process exceeded the limit | Set a realistic cURL/process timeout, wait on a specific readiness condition, and reduce unnecessary third-party requests. |
| wkhtmltopdf exits nonzero on a server | Binary path, permissions, missing libraries or an X-server-dependent feature | Use an absolute pinned path, capture stderr, verify executable permissions and remove features that require a display service. |
| PDF response is corrupted when streamed | Output was preceded by PHP warnings, notices or whitespace | Disable display errors for the endpoint, clean the response path, set headers first and send only the binary. |
Performance, reliability and cost decisions
- Local binaries: avoid a per-request vendor charge, but require image size limits, process supervision, sandboxing, font installation and browser/binary upgrades.
- Managed APIs: reduce operations work and usually offer explicit readiness and PDF controls, but add authentication, network latency, provider limits and recurring usage charges.
- Retries: retry transient network or renderer failures with a bounded backoff. Do not blindly retry invalid URLs, authentication errors or deterministic layout failures.
- Caching: cache PDFs for unchanged inputs when freshness permits; include the URL, relevant data version and rendering options in the cache key.
- Security: treat URL-to-PDF endpoints as server-side request forgery surfaces. Restrict destinations, protect credentials, limit output size and isolate browser processes when users can submit URLs.
Measure your own pages for latency and memory. The available vendor documentation describes capabilities and examples but does not provide a shared benchmark across engines.
Or skip the browser setup
ScreenshotNeo is the first service to try when you want an API rather than a browser installation: it removes consent banners, newsletter popups and chat widgets before capture, bills only clean shots, and offers an MCP server for AI agents. Its API can return PNG, JPEG, WebP or PDF; the controls include full-page capture with lazy images loaded, CSS-selector element capture, device and viewport settings, retina scale, print options, custom CSS and JavaScript, click and wait actions, request blocking, headers, cookies, user agents, timezone and geolocation, caching, signed links, asynchronous webhooks, bulk capture and a usage API.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemsUse the documented PDF option when you need PDF output. The following one-call examples use the endpoint and parameters shown in the ScreenshotNeo documentation:
cURL
curl -G 'https://api.screenshotneo.com/v1/shot' -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/report -o shot.webp
Python
import requests
r = requests.get('https://api.screenshotneo.com/v1/shot', params={'access_key': 'YOUR_API_KEY', 'url': 'https://example.com/report'}, 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://example.com/report' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
Cookie banners, popups and chat widgets are removed before the shot. Bot checks, blank pages, failed loads and cache hits are not billed, and each response identifies the result with X-Page-Verdict and X-Billed headers. The MCP server exposes take_screenshot, get_page_info and capture_pdf for Claude, Cursor and other MCP clients.
| Plan | Allowance | Price |
|---|---|---|
| Free | 1,000 shots/month | $0; no card |
| Starter | 3,000 shots | $5 |
| Growth | 15,000 shots | $15 |
| Pro | 60,000 shots | $39 |
| Scale | 250,000 shots | $99 |
| Business | 1,000,000 shots | $249 |
Every feature is included on every plan, and yearly billing gives two months free. Start with 1,000 free screenshots a month without a card.
FAQ
Does a PDF preserve the page’s interactive behavior?
No. JavaScript can be used during rendering to produce the final layout, but the delivered PDF is a static document. Build links and visible controls into the print output if readers need navigation.
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 →Should I render from a URL or submit HTML?
Use a URL when the renderer can reach the page and its assets. Submit HTML when the page is private, generated per request or dependent on server-side session state, while providing a base URL or absolute asset links.
How do I keep repeated headers readable across pages?
Use the renderer’s header template support when available, or design a print-specific header and test it with explicit top margins. A screen-positioned fixed header should not be relied on for paginated output.
Frequently Asked Questions
Can a PDF include clickable links?
Yes, when the selected renderer’s link-handling option is enabled; verify both internal anchors and external URLs in the generated file.
Why does the first page look correct while later pages break?
Pagination exposes print-only issues such as fixed elements, table rows that cannot split, and missing page-break rules. Add print CSS and test a document long enough to cross each intended break.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Is a successful HTTP status proof that conversion worked?
No. Validate the content type, nonzero length and PDF signature before saving or streaming, because an endpoint can return an error document in the response body.
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.




