Use wkhtmltoimage from PHP by installing a verified binary, pointing a PHP wrapper at its absolute path, and passing a URL or HTML document to an image-generation method. The most maintainable approach is KnpLabs Snappy; Symfony applications can use KnpSnappyBundle. The examples below cover PNG and JPEG output, local HTML, JavaScript timing, authentication, security, troubleshooting, and production deployment.
What wkhtmltoimage does
wkhtmltoimage is an open-source LGPLv3 command-line utility from the wkhtmltopdf project. It renders a URL or local HTML file with the Qt WebKit engine and writes an image such as PNG or JPEG. It runs headlessly, so a display server is not required. The general command is:
wkhtmltoimage [OPTIONS]... <input file> <output file>
The output extension normally selects the format, but confirm the exact formats and switches supported by the binary installed on your server with wkhtmltoimage --extended-help. Its WebKit engine is older than current Chromium- or Firefox-based browsers, so modern JavaScript and CSS may require changes or a different renderer.
Install and verify the binary
Linux and macOS-style environments
Install a wkhtmltopdf distribution that includes wkhtmltoimage, or build the project from source. Then verify the executable as the same operating-system user that will run PHP:
#1 Best Overall
which wkhtmltoimage
wkhtmltoimage --version
wkhtmltoimage --extended-help
Record the path returned by which, such as /usr/local/bin/wkhtmltoimage. Linux installations also need the fonts and shared libraries expected by the selected binary. A command that works in your shell can still fail under PHP-FPM if that service account has a different PATH, home directory, permissions, or library environment.
Windows
Install a distribution containing wkhtmltoimage.exe and make the wkhtmltox DLL available through PATH, as required by the PHP integration. Prefer an absolute Windows path in your PHP configuration, for example C:\Program Files\wkhtmltopdf\bin\wkhtmltoimage.exe, and test it under the identity used by the web server.
Containers and packaged binaries
When native libraries are difficult to maintain, a maintained PHP packaging project documents bundled binaries and a Docker fallback. Treat the image tag, CPU architecture, operating-system libraries, and fonts as deployment inputs: pin them, test them in your CI environment, and do not assume a container built for one architecture will run on another.
Smoke-test wkhtmltoimage before adding PHP
Start with a public URL and a fixed viewport:
wkhtmltoimage --format png --width 1280 https://example.com /tmp/example.png
Open /tmp/example.png and inspect the command’s exit status. For local assets, explicitly allow only the directory that contains the document and its resources:
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →wkhtmltoimage --enable-local-file-access
--allow /var/www/app/public
/var/www/app/public/card.html
/tmp/card.png
Keep local-file access disabled unless it is necessary. Enabling it for untrusted HTML or JavaScript can expose server files and, in unsafe designs, contribute to remote-code-execution risk.
Choose a PHP integration
KnpLabs Snappy: the general-purpose wrapper
Snappy gives PHP an object-oriented interface, option handling, temporary-file management, and methods that return rendered bytes. Install it with Composer:
composer require knplabs/knp-snappy
The following script renders both a URL and an HTML string:
Rank #2
<?php
require __DIR__ . '/vendor/autoload.php';
use KnpSnappyImage;
$image = new Image('/usr/local/bin/wkhtmltoimage');
$image->setOption('format', 'png');
$image->setOption('width', 1280);
$image->setOption('javascript-delay', 300);
$image->generate(
'https://example.com',
__DIR__ . '/var/example.png'
);
$html = '<!doctype html><html><body><h1>Invoice</h1></body></html>';
$image->generateFromHtml($html, __DIR__ . '/var/invoice.png');
Create the destination directory ahead of time and grant the PHP service account write access to it. Use a different output extension and format value for JPEG.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Symfony KnpSnappyBundle
Install the bundle:
composer require knplabs/knp-snappy-bundle
Configure the image binary separately from any PDF binary:
# config/packages/knp_snappy.yaml
knp_snappy:
image:
enabled: true
binary: /usr/local/bin/wkhtmltoimage
options:
format: png
width: 1280
process_timeout: 20
Inject the image service and return its bytes from a controller:
public function card(KnpSnappyImage $knpSnappyImage): Response
{
$html = $this->renderView('card.html.twig', ['name' => 'Ada']);
return new JpegResponse(
$knpSnappyImage->getOutputFromHtml($html),
'card.jpg'
);
}
Use a direct process call only for a very small integration where you are prepared to implement escaping, temporary files, timeouts, and error handling yourself. A wrapper reduces that plumbing but does not make untrusted input safe.
Render a URL, a local file, or an HTML string
Remote URL
Pass an absolute URL to generate(). The server running wkhtmltoimage must be able to resolve DNS, establish the connection, and reach the target over its firewall and proxy configuration.
Recommended Free Tools
Local HTML
For a local file, use an absolute path and the smallest possible --allow directory. Keep stylesheets, images, and fonts below that directory and reference them with readable absolute or appropriately resolved paths.
HTML generated by PHP
generateFromHtml() is convenient for invoices, cards, and reports. Escape user data before inserting it into markup, and never let a request choose arbitrary filesystem paths or renderer flags.
Options that matter in real applications
Option names and availability vary by release; inspect --extended-help on the target host. Snappy accepts individual settings or an array:
$image->setOptions([
'format' => 'jpeg',
'quality' => 88,
'width' => 1200,
'javascript-delay' => 500,
'load-error-handling' => 'ignore',
]);
| Need | Relevant controls | Implementation note |
|---|---|---|
| Dimensions | --width, --height |
Set a deterministic viewport; height and full-page behavior depend on the document and binary version. |
| Cropping | --crop-x, --crop-y, --crop-w, --crop-h |
Use when the required output is a region rather than the whole rendered page. |
| Format and compression | --format, --quality |
PNG is lossless; JPEG quality changes file size and visual detail. |
| Client rendering | JavaScript enable/disable, --javascript-delay |
Use a bounded delay, or a deterministic render-complete signal controlled by the page. |
| Authenticated pages | Cookies and custom headers | Pass only the credentials needed for the request and avoid logging them. |
| Networking | Proxy settings and request headers | Match the network policy of the PHP service account. |
| Failures | --load-error-handling |
Choose deliberately; ignoring errors can produce an incomplete image. |
Charts and client-rendered widgets often need JavaScript enabled and a delay long enough for data and layout to settle. A page-controlled window.status or equivalent render-complete signal is more deterministic than continually increasing a sleep, when your page can provide one.
Free tools Windows power users keep installed
One-click scans. No signup required.
Make the integration safe
- Sanitize all user-controlled HTML and data before rendering.
- Do not pass arbitrary command-line options or paths from a request.
- Keep
--enable-local-file-accessoff by default; if required, restrict--allowto a dedicated directory. - Run the renderer as a low-privilege account, not as root.
- Use AppArmor, SELinux, or container isolation where practical.
- Set a process timeout and limit page size, resource loading, and concurrency.
- Store secrets outside logs, including cookies, Authorization headers, and generated command lines.
Troubleshooting: symptom, cause, and fix
“Executable not found”
PHP-FPM may not inherit your shell’s PATH. Run which wkhtmltoimage as the PHP service account and configure that absolute path in Snappy or KnpSnappyBundle.
Exit code 126 or permission denied
Make the file executable and verify that its filesystem is not mounted with execution disabled. Confirm that every parent directory is searchable by the service account.
Blank output, missing text, or incorrect fonts
Install the fonts and shared libraries required by the binary. Compare CLI output under the same account, environment, and working directory used by PHP. Missing fonts can change line wrapping and image dimensions even when the command succeeds.
CSS or images from a local file are missing
Local access is disabled by default in many builds. Add only the required directory with --allow, use readable paths, and do not solve the problem by allowing the entire filesystem.
JavaScript content is absent
Check that JavaScript has not been disabled, add a bounded javascript-delay, and inspect browser-console assumptions in the page. The legacy Qt WebKit engine may not implement modern JavaScript APIs, modules, or CSS features used by a current browser.
Rank #4
The request hangs or times out
Set the wrapper’s process timeout, cap resource loading and page size, and move expensive renders to a queue rather than blocking an ordinary web request. A timeout should terminate the child process and clean up temporary files.
Authenticated content renders as a login page
Pass the required cookie or header explicitly, verify its scope and expiration, and ensure redirects are reachable from the server. Never print the credential while debugging.
Performance, reliability, and maintenance
Rendering starts a separate process, so throughput depends on CPU, memory, page complexity, network latency, fonts, and the number of concurrent workers. Reuse stable templates, avoid unbounded user content, and queue bursts. Set a fixed viewport and renderer version so output changes are visible rather than accidental.
The upstream wkhtmltopdf repository is archived and read-only. Treat wkhtmltoimage as a compatibility-bound legacy renderer: pin the binary version, operating-system image, and fonts; retain a representative visual-regression sample; and test after any library or container change. A maintained packaging project documents 0.12.6.1 binaries and a Docker fallback, but architecture and shared-library compatibility still need to be checked in your environment.
Choose wkhtmltoimage when compatibility with an existing Qt WebKit workflow matters and its rendering limits are acceptable. If your pages depend on current browser APIs, evaluate a maintained modern-browser renderer instead of assuming that a successful process exit means visual fidelity.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF, so PHP does not need a locally installed browser binary. The API accepts the URL and access key as query parameters:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
In PHP, the same request can be made with cURL or any HTTP client:
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated 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 match<?php
$url = 'https://api.screenshotneo.com/v1/shot';
$query = http_build_query([
'access_key' => 'YOUR_API_KEY',
'url' => 'https://stripe.com',
]);
$ch = curl_init($url . '?' . $query);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 90,
]);
$bytes = curl_exec($ch);
if ($bytes === false) {
throw new RuntimeException(curl_error($ch));
}
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($status < 200 || $status >= 300) {
throw new RuntimeException('Screenshot API returned HTTP ' . $status);
}
file_put_contents(__DIR__ . '/shot.webp', $bytes);
See the ScreenshotNeo API documentation for all parameters. It can accept cookie and consent banners like a visitor and remove more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and whether it was billed.
Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients. Other options include full-page capture with lazy images loaded, CSS-selector element capture, dark mode, device presets and custom viewports, retina scale, PDF paper and page controls, custom CSS and JavaScript, clicks, waits, blocked resources, headers, cookies, user agents, Authorization, timezone, geolocation, transparent backgrounds, resizing, chosen-TTL caching, signed image links, asynchronous jobs with signed webhooks, bulk capture of 100 URLs per call, a usage API, OpenAPI, and compatibility with parameter names used by other screenshot APIs.
| Plan | Included shots | Price |
|---|---|---|
| Free | 1,000 per month | $0, no card |
| Starter | 3,000 | $5 |
| Growth | 15,000 | $15 |
| Pro | 60,000 | $39 |
| Scale | 250,000 | $99 |
| Business | 1,000,000 | $249 |
Yearly billing gives two months free, and every feature is available on every plan. Create a free ScreenshotNeo account to get 1,000 screenshots per month without a card.
FAQ
Can wkhtmltoimage create a PDF?
No. wkhtmltoimage is the image utility; use wkhtmltopdf for PDF output or an API such as ScreenshotNeo’s PDF capture when that is the required artifact.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsIs a display server such as Xvfb required?
The utility is designed to run headlessly, so a display service is not required. Fonts, shared libraries, permissions, and network access still must be present.
Which PHP version does KnpLabs Snappy support?
Packagist listed KnpLabs Snappy v1.7.3 with a PHP 8.1-or-newer requirement and a 2026-07-29 release date. Confirm the version selected by Composer in your project before deployment.
Why does a successful command produce a different image after an upgrade?
Rendering can change with the binary, operating-system libraries, fonts, page assets, or timing. Pin those inputs and compare a visual-regression sample during upgrades.
Frequently Asked Questions
Can I render a page that requires a login?
Yes, when you deliberately supply the required cookies or headers and keep those credentials out of logs. Verify redirects and session expiry from the server running the renderer.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Should I use wkhtmltoimage for untrusted HTML?
Only with a strong isolation boundary. Sanitize markup, restrict local-file access and allowed paths, run as a low-privilege user, and add operating-system or container sandboxing.
What is the simplest way to return the image from Symfony?
Use KnpSnappyBundle’s injected image service and return the bytes from getOutputFromHtml() or generate() through the response class appropriate to the selected format.
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.

