Start by identifying which permission is failing. If the shell cannot start the wkhtmltopdf executable, exit status 126 and a message such as sh: /path/to/wkhtmltopdf: Permission denied indicate an operating-system execution problem. If wkhtmltopdf starts but the PDF is missing CSS, images or fonts, the executable permission is not the issue: check local-file access and the account’s ability to read each asset. Finally, verify that the same service account can read the input, create temporary files and write the output.
This guide covers wkhtmltopdf 0.12.6 with patched Qt, including command-line use and framework wrappers.
1. Classify the failure before changing permissions
Run the command manually and record both its exit status and stderr. These symptoms point to different layers:
| What you see | Likely layer | First check |
|---|---|---|
Shell says Permission denied; exit status 126 |
Binary execution | Executable path, mode, ownership, parent-directory traversal, mount options and architecture |
| PDF is created, but local CSS, images or fonts are absent | Local-resource policy or asset permissions | --allow, file paths and the service account’s read access |
| Input cannot be opened | HTML read permission or wrong path | Absolute input path and read access as the worker account |
| Output cannot be created | Destination or temporary-directory permissions | Parent directory write/execute access and available space |
| Works in a shell but fails in a web job | Integration environment | Configured binary, user, PATH, WKHTMLTOPDF_ENV and working directory |
Do not respond to every symptom with chmod 755. That change cannot fix a noexec mount, an inaccessible parent directory, a wrong architecture, a missing dynamic loader or a wrapper that invokes a different binary.
#1 Best Overall
2. Fix an executable that will not start
Confirm the exact binary
Find the file your application is actually configured to run. A shell lookup and an explicit path check prevent you from fixing an unused copy:
command -v wkhtmltopdf
readlink -f "$(command -v wkhtmltopdf)"
ls -l /absolute/path/to/wkhtmltopdf
file /absolute/path/to/wkhtmltopdf
If your framework supports an absolute setting, use it. django-wkhtmltopdf, for example, documents WKHTMLTOPDF_CMD. Set that value to the intended executable rather than relying on an uncertain PATH.
Inspect mode and ownership
The account launching the process needs execute permission on the file and search (execute) permission on every directory in the path. Inspect each component:
namei -l /absolute/path/to/wkhtmltopdf
stat /absolute/path/to/wkhtmltopdf
Grant the narrowest access that matches your deployment. If the file is not executable, correct its mode or reinstall it from a trusted package. Then test as the actual worker account, not as your interactive user:
Recommended Free Tools
Rank #2
sudo -u www-data /absolute/path/to/wkhtmltopdf --version
Replace www-data with the user shown by your process manager, queue worker or container. If this still returns status 126, inspect the filesystem mount:
findmnt -T /absolute/path/to/wkhtmltopdf -o TARGET,OPTIONS
mount | grep noexec
A binary stored on a filesystem mounted noexec cannot be launched even when its mode is 755. Move it to an executable filesystem or change the mount policy according to your operating-system controls. Also verify that the binary architecture matches the host and that required libraries and the interpreter are present; file and your distribution’s dependency tools can reveal those problems.
Check confinement controls
AppArmor, SELinux, container profiles and service sandboxes can deny execution while ordinary Unix permissions look correct. Review the service logs for a denial and allow only the required binary and directories. Do not disable mandatory access controls as a first fix.
3. Test input, output and temporary directories
wkhtmltopdf must read the HTML input and write the destination PDF. In a web application it may also create temporary files. Use absolute paths and test every operation under the real account:
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →sudo -u www-data test -r /srv/app/invoice.html && echo input-readable
sudo -u www-data test -w /srv/app/output && echo output-writable
sudo -u www-data sh -c 'tmp=$(mktemp /srv/app/tmp/wkhtmltopdf.XXXXXX) && rm "$tmp"'
For a direct diagnostic run:
sudo -u www-data /absolute/path/to/wkhtmltopdf
/srv/app/invoice.html /srv/app/output/invoice.pdf
Check the parent directories, not only the file itself: a process needs directory search permission to reach a file and write permission on the destination directory to create a new PDF. Confirm free space and inode availability if the error appears only for larger documents. A relative path can also resolve differently in a web worker, cron job or systemd unit, so prefer absolute paths while diagnosing.
4. Restore local CSS, images and fonts safely
Patched-Qt wkhtmltopdf applies a separate local-file policy. The documented controls are:
--disable-local-file-access— disables access to local files (the patched usage text lists this as the default).--allow <path>— permits access to a specific directory.--enable-local-file-access— enables broad local-file access.
If the process starts and the PDF is otherwise produced, use a narrow allow-list first:
/absolute/path/to/wkhtmltopdf
--allow /srv/app/public/assets
/srv/app/invoice.html /srv/app/output/invoice.pdf
Ensure the worker can traverse /srv, /srv/app, /srv/app/public and /srv/app/public/assets, and can read the referenced files. Use URLs or file paths that match the renderer’s actual environment; a browser on your laptop may resolve a path that does not exist inside a container.
Rank #4
Use --enable-local-file-access only when the HTML is trusted and broad access is genuinely required. Keeping the default restriction and granting one asset root limits accidental exposure. If you intentionally need local resources while retaining tighter policy, avoid granting the entire filesystem.
5. Make framework and service environments explicit
Wrappers can select another executable, working directory or environment. Log the final command and non-secret environment values during a controlled diagnostic request. For django-wkhtmltopdf, set WKHTMLTOPDF_CMD to the absolute binary and use WKHTMLTOPDF_ENV for required overrides. If you use --use-xserver, the documentation specifically calls out DISPLAY as an environment value that may need to be supplied.
Compare an interactive success with the failing job:
- Which Unix user and group runs each command?
- What is
PATHand the current working directory? - Is the configured binary inside the same container, virtual environment or host?
- Are input, output and temporary directories mounted in the job?
- Are security profiles or systemd sandbox directives different?
Keep API keys, cookies and other secrets out of command and environment logs. Once the path is proven, remove verbose diagnostics or restrict them to administrators.
Free tools Windows power users keep installed
One-click scans. No signup required.
Best Value
6. A repeatable diagnostic checklist
- Capture the complete stderr and numeric exit status.
- Resolve the exact executable configured by the shell or framework.
- Run
file,statandnamei -lon that path. - Execute
wkhtmltopdf --versionas the real service account. - Check mount options, mandatory-access-control logs and container policy.
- Test read access to the HTML and every parent directory.
- Test write access and temporary-file creation in the destination and temp directories.
- If assets are missing, add a specific
--allowdirectory and retest. - Only for trusted HTML, consider
--enable-local-file-access. - Set framework command and environment variables explicitly, then restart the worker so configuration is actually loaded.
7. Security: permission fixes can increase the blast radius
The wkhtmltopdf project warns: “Do not use wkhtmltopdf with any untrusted HTML – be sure to sanitize any user-supplied HTML/JS, otherwise it can lead to complete takeover of the server it is running on!” Treat HTML, JavaScript, CSS and referenced URLs as code-like input. Sanitize user content, run the renderer under a dedicated low-privilege account, restrict network and filesystem access, and consider AppArmor or SELinux. A broad local-file allowance can expose secrets if untrusted markup is rendered, so prefer a dedicated asset directory and --allow.
8. When to use another renderer
wkhtmltopdf 0.12.6 remains useful for compatible, controlled documents, but its older rendering stack may not suit every modern HTML or JavaScript workload. The project status guidance names WeasyPrint, Prince and Puppeteer as alternatives for some use cases. Compare them on the browser features your templates require, deployment model, licensing and maintenance expectations; switching will not remove the need to secure untrusted HTML.
Or skip the browser setup
If your real goal is a clean screenshot or PDF of a web page rather than server-side HTML-to-PDF rendering, ScreenshotNeo provides a single API request. It accepts 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 the response identifies the result with X-Page-Verdict and X-Billed headers. An MCP server provides take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients.
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)
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}`);
See the complete parameter reference in the ScreenshotNeo documentation. Every plan includes its features; the Free plan provides 1,000 screenshots per month with no card, and paid plans start at $5 for 3,000 shots. Sign up free to try it.
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 errorsFrequently Asked Questions
What does exit status 126 mean for wkhtmltopdf?
It normally means the shell found the command but could not execute the file. Check the exact path, parent-directory traversal, mount options, architecture, loader and service-account restrictions.
Why are remote images visible but file:// images missing?
Remote and local resources follow different rules. Verify the asset path and read permissions, then use a narrowly scoped --allow directory or, only for trusted HTML, --enable-local-file-access.
Should I make wkhtmltopdf world-executable?
No. Give the dedicated worker only the execute, read and directory-traverse permissions it needs, and preserve AppArmor, SELinux or container restrictions.
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.
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 →Clear out junk files and repair common Windows errorsFree Scan →

