How to Take Screenshots with wkhtmltopdf
Use wkhtmltoimage—not wkhtmltopdf—to save webpage screenshots. Learn viewport, crop, JavaScript waits, security limits, and a hosted alternative.
Use wkhtmltoimage to create a screenshot image. The similarly named wkhtmltopdf executable creates PDF documents. Both are command-line tools built on a headless Qt WebKit renderer, but they produce different output types.
The basic image command is:
wkhtmltoimage https://example.com screenshot.png
This follows the documented syntax wkhtmltoimage [OPTIONS]... <input file> <output file>. See the official wkhtmltoimage manual for the complete option list.
1. Choose the correct executable
| Command | Output | Use it when |
|---|---|---|
wkhtmltoimage |
PNG, JPEG, BMP or SVG | You need a raster or vector image of a webpage. |
wkhtmltopdf |
You need a paginated document for printing, archiving or download. |
If a script currently calls wkhtmltopdf page.html output.png, change the executable to wkhtmltoimage. A PDF executable should not be used as an image screenshot command.
2. Install wkhtmltoimage
Download a package for your operating system from the project’s official downloads page. The listed stable release is 0.12.6, released June 11, 2020. Package availability depends on your platform.
Confirm that the image executable is installed:
wkhtmltoimage --version
which wkhtmltoimage
On Windows, run wkhtmltoimage.exe from its installation directory or add that directory to PATH. In a container or CI job, install the package during image creation and verify the binary before running captures.
3. Capture a basic webpage
wkhtmltoimage https://example.com screenshot.png
The first argument can be an HTTP(S) URL or a local HTML file. The final argument is the output filename. The extension normally matches the selected format, but you can explicitly choose one with --format.
Save JPEG, PNG, BMP or SVG
wkhtmltoimage --format png https://example.com example.png
wkhtmltoimage --format jpg https://example.com example.jpg
wkhtmltoimage --format bmp https://example.com example.bmp
wkhtmltoimage --format svg https://example.com example.svg
Use PNG for sharp text and screenshots with transparency, JPEG for smaller photographic images, BMP for workflows that require an uncompressed bitmap, and SVG only when the resulting vector output fits your downstream tooling.
4. Set the viewport and page size
The viewport controls the width and height used while rendering:
wkhtmltoimage \
--width 1440 \
--height 900 \
https://example.com desktop.png
--width is a rendering guide unless strict-width behavior is enabled by the build and configuration. The renderer may also use smart-width behavior. Set an explicit width when responsive breakpoints matter, and compare captures at each target width.
For a mobile-style capture:
wkhtmltoimage --width 390 --height 844 https://example.com mobile.png
A tall page may extend beyond the requested height. Use cropping when you need a fixed rectangle rather than the whole rendered page.
5. Crop a region
Crop coordinates are measured from the rendered page’s top-left corner:
wkhtmltoimage \
--crop-x 100 \
--crop-y 200 \
--crop-w 800 \
--crop-h 600 \
https://example.com region.png
| Option | Meaning |
|---|---|
--crop-x |
Horizontal starting coordinate. |
--crop-y |
Vertical starting coordinate. |
--crop-w |
Crop width. |
--crop-h |
Crop height. |
Crop after choosing the viewport. Responsive layouts, browser scrollbars and device scale can change the coordinates you expect.
6. Control JPEG quality
For JPEG output, --quality accepts a documented range from 0 to 100:
wkhtmltoimage --format jpg --quality 85 https://example.com page.jpg
Higher values preserve more detail and produce larger files. This setting applies to JPEG image output; it is separate from image-quality settings used by the PDF command.
7. Wait for JavaScript and asynchronous content
JavaScript is enabled by default. Pages that render after the initial load may need a delay:
wkhtmltoimage \
--javascript-delay 3000 \
https://example.com dashboard.png
The delay is measured after the page load phase. It does not prove that an application finished every network request or animation. Use the shortest delay that consistently produces complete output.
A page can signal readiness by setting window.status. Wait for that value with:
wkhtmltoimage \
--window-status screenshot-ready \
https://example.com report.png
Your page must set the value before capture, for example:
<script>
fetch('/data.json')
.then(() => { window.status = 'screenshot-ready'; });
</script>
Some modern applications still fail even with a delay or status value because the embedded WebKit engine is old and does not implement current browser APIs or JavaScript behavior.
8. Capture local HTML safely
wkhtmltoimage file:///absolute/path/page.html local.png
Local pages that reference neighboring CSS, images or fonts may require local file access. The manual documents disabling local file access and an allow option for explicitly permitted paths. Grant access only to directories the page needs:
wkhtmltoimage \
--allow /absolute/path/assets \
file:///absolute/path/page.html local.png
Do not enable broad filesystem access for untrusted HTML. A local page can potentially read files available to the rendering process.
9. A reusable shell script
#!/usr/bin/env bash
set -euo pipefail
url="${1:?Usage: $0 URL OUTPUT}"
output="${2:?Usage: $0 URL OUTPUT}"
wkhtmltoimage \
--format png \
--width 1440 \
--height 900 \
--javascript-delay 1500 \
"$url" \
"$output"
echo "Saved $output"
Quote both variables so URLs containing query strings and output paths containing spaces are passed as single arguments. Add validation before invoking this script if URLs come from users.
10. Common failures and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
command not found |
The package is missing or the binary is not on PATH. |
Install from the official downloads page, locate wkhtmltoimage, or use its absolute path. |
| Blank or partially rendered image | Content is loaded asynchronously. | Increase --javascript-delay, use --window-status, or choose a renderer with current browser support. |
| Modern CSS or JavaScript is missing | The bundled Qt WebKit engine is old. | Simplify the page for this renderer or use a modern browser automation tool such as Puppeteer, as the maintainer suggests for dynamic JavaScript pages. |
| Images or styles fail on a local file | Local file access is restricted or paths are wrong. | Use absolute paths and allow only the required asset directory with --allow. |
| Output has the wrong dimensions | Responsive breakpoints, smart-width behavior or crop coordinates changed the layout. | Set --width and --height explicitly, then recalculate crop values for that viewport. |
| JPEG is too large or blurry | Quality is mismatched to the image content. | Adjust --quality; use PNG when text sharpness matters more than file size. |
| Fonts differ from the browser | The font is unavailable in the capture environment. | Install the required fonts or bundle them in the page, then capture in the same environment used for deployment. |
| Capture hangs | A page keeps connections open or never reaches the expected state. | Remove the status wait, use a bounded delay, add process-level timeouts, and inspect the URL separately. |
| Security review blocks the design | Untrusted HTML or JavaScript is being rendered. | Do not pass it directly to wkhtmltoimage. Sanitize input and isolate the process with least-privilege permissions and network/filesystem controls. |
11. Reliability, security and tool choice
The project’s status page says Qt 4 has been unsupported since 2015 and that its WebKit had not been updated since 2012. The downloads page lists version 0.12.6 from 2020. This age matters for sites that depend on current browser APIs, modern TLS behavior, complex JavaScript bundles or current CSS.
The project explicitly warns against using wkhtmltopdf with untrusted HTML because unsanitized user HTML or JavaScript can lead to complete server takeover. Treat the same rendering process as unsafe for untrusted content: sanitize input, run it in an isolated worker, use a dedicated low-privilege account, restrict filesystem access, limit outbound network access where practical, and enforce CPU, memory and execution time limits.
For controlled report generation, the maintainer names WeasyPrint and Prince as alternatives. For pages that depend on dynamic JavaScript, the maintainer suggests Puppeteer or one of its wrappers. Choose based on the page’s requirements rather than assuming every site will render identically.
12. Performance and cost considerations
- Set a viewport that matches the deliverable; unnecessarily large captures consume more memory and produce larger files.
- Use the smallest reliable JavaScript delay. Long fixed waits increase latency on every request.
- Reuse a warm worker process where your deployment model allows it, but recycle workers that leak memory or become stuck.
- Cache identical URL and option combinations when the page does not change frequently.
- Record the URL, options, renderer version, exit code and elapsed time so failures can be reproduced.
- For parallel jobs, cap concurrency according to available CPU and memory. More processes can reduce throughput when each renderer competes for the same resources.
wkhtmltoimage itself is local command-line software, so your direct cost is the machine and operational work required to run it. The older engine can also create indirect costs when a site needs workarounds or a second renderer.
13. Or skip the browser setup
ScreenshotNeo provides a hosted screenshot API. One GET request returns a PNG, JPEG, WebP or PDF. The API accepts the same kinds of URL and capture controls developers commonly need, including viewport and full-page capture, element selectors, dark mode, custom CSS and JavaScript, click actions, waits, blocked resources, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, caching, signed links, asynchronous jobs, webhooks, bulk capture and usage reporting. Its documentation lists the request options and OpenAPI specification.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
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()));
Cookie banners, newsletter popups and chat widgets are removed before the shot. Bot checks, blank pages, timeouts, failed loads and cache hits are not billed, and response headers report the page verdict and whether the request was billed. ScreenshotNeo also provides an MCP server with take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.
Start with 1,000 free screenshots a month—no card required.
14. FAQ
Can wkhtmltopdf take a PNG screenshot?
No. Use the companion wkhtmltoimage executable for image output. Use wkhtmltopdf when the required output is a PDF.
Does wkhtmltoimage execute JavaScript?
JavaScript is enabled by default. Use --javascript-delay or --window-status for asynchronous pages, but the old WebKit engine may not support modern applications.
How do I capture only one part of a page?
Use --crop-x, --crop-y, --crop-w and --crop-h after setting the viewport.
Is wkhtmltoimage safe for user-submitted HTML?
Do not treat it as safe by default. Sanitize untrusted input and isolate the renderer because the project warns that malicious HTML or JavaScript can compromise the server.
When should I use a different renderer?
Use a modern browser automation tool for sites that depend on current JavaScript and browser APIs. For controlled HTML-to-PDF report generation, consider the alternatives named on the project status page.


