How to Configure KnpSnappyBundle Options
Configure KnpSnappyBundle for PDF and image output, set renderer options safely, troubleshoot failures, and choose the right runtime setup.

Direct answer: configure KnpSnappyBundle in config/packages/knp_snappy.yaml. Define separate pdf and image sections, point them to the actual wkhtmltopdf and wkhtmltoimage executables, and put renderer flags in each section’s options array.
# config/packages/knp_snappy.yaml
knp_snappy:
pdf:
enabled: true
binary: /usr/local/bin/wkhtmltopdf
options: []
image:
enabled: true
binary: /usr/local/bin/wkhtmltoimage
options: []
Use the real paths from the environment where Symfony runs. The paths above are examples only.
1. Install the bundle
composer require knplabs/knp-snappy-bundle
With Symfony Flex, the bundle is normally registered by the recipe. Without Flex, add it to config/bundles.php:
<?php
return [
Knp\Bundle\SnappyBundle\KnpSnappyBundle::class => ['all' => true],
];
2. Configure PDF and image services
The two services are independent. Enable only the renderer your application needs.

| Section | Executable | Typical output |
|---|---|---|
pdf |
wkhtmltopdf |
PDF documents |
image |
wkhtmltoimage |
JPEG, PNG or other formats supported by the installed binary |
knp_snappy:
pdf:
enabled: true
binary: '%env(WKHTMLTOPDF_PATH)%'
options:
page-size: A4
margin-top: 12mm
margin-right: 12mm
margin-bottom: 12mm
margin-left: 12mm
image:
enabled: false
binary: '%env(WKHTMLTOIMAGE_PATH)%'
options:
format: png
width: 1440
Environment variables keep machine-specific executable paths out of committed configuration:
# .env.local
WKHTMLTOPDF_PATH=/usr/local/bin/wkhtmltopdf
WKHTMLTOIMAGE_PATH=/usr/local/bin/wkhtmltoimage
On Windows, quote paths containing spaces and escape them according to your YAML syntax:
knp_snappy:
pdf:
enabled: true
binary: 'C:\\Program Files\\wkhtmltopdf\\bin\\wkhtmltopdf.exe'
options: []
3. Set temporary storage and process limits
KnpSnappyBundle uses sys_get_temp_dir() by default. Set temporary_folder when the default directory is not writable, is too small, or should be isolated per application. process_timeout is measured in seconds.
knp_snappy:
temporary_folder: '%kernel.cache_dir%/snappy'
process_timeout: 20
pdf:
enabled: true
binary: '%env(WKHTMLTOPDF_PATH)%'
options: []
image:
enabled: true
binary: '%env(WKHTMLTOIMAGE_PATH)%'
options: []
Create the directory and ensure the PHP worker user can write to it. Choose a timeout based on your page size, remote assets and queue or HTTP deadline; 20 seconds is an example, not a universal recommendation.
4. Pass wkhtmltopdf and wkhtmltoimage options
The options array becomes renderer arguments. Option names are written without the leading --. For example, disable-javascript: true maps to --disable-javascript.
knp_snappy:
pdf:
enabled: true
binary: '%env(WKHTMLTOPDF_PATH)%'
options:
disable-javascript: false
no-background: false
allow:
- '%kernel.project_dir%/public'
cookie:
- 'session_id abc123'
cache-dir: '%kernel.cache_dir%/wkhtmltopdf'
image:
enabled: true
binary: '%env(WKHTMLTOIMAGE_PATH)%'
options:
format: jpeg
quality: 90
width: 1600
javascript-delay: 500
Options such as disable-javascript, no-background, allow, cookie, post, cover, toc and cache-dir are renderer examples. Exact behavior depends on the installed binary, so inspect its help output and version before relying on a flag.
wkhtmltopdf --version
wkhtmltopdf --extended-help
wkhtmltoimage --version
wkhtmltoimage --extended-help
5. Use the bundle services in Symfony
The integration exposes knp_snappy.pdf and knp_snappy.image. Generate from a URL or from HTML.
Generate a PDF from a URL
<?php
namespace App\Controller;
use Knp\Snappy\Pdf;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
final class InvoiceController
{
#[Route('/invoice/{id}.pdf')]
public function pdf(string $id, Pdf $pdf): Response
{
$url = 'https://example.com/invoices/' . rawurlencode($id);
$content = $pdf->getOutput($url);
return new Response($content, 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'inline; filename="invoice-' . $id . '.pdf"',
]);
}
}
Generate from rendered HTML
use Knp\Snappy\Pdf;
use Symfony\Component\HttpFoundation\Response;
public function statement(Pdf $pdf): Response
{
$html = $this->renderView('statement/pdf.html.twig', [
'account' => $account,
]);
return new Response(
$pdf->getOutputFromHtml($html),
200,
['Content-Type' => 'application/pdf']
);
}
Generate an image
use Knp\Snappy\Image;
use Symfony\Component\HttpFoundation\Response;
public function preview(Image $image): Response
{
$bytes = $image->getOutputFromHtml('<h1>Preview</h1>');
return new Response($bytes, 200, [
'Content-Type' => 'image/jpeg',
]);
}
For larger files, write output to a controlled temporary path and stream it instead of holding multiple documents in memory.
6. Choose options by workload
- PDF layout: set page size, margins, orientation and any header or footer options supported by your binary.
- Images: set format, quality, width or height, and a delay when client-side rendering needs time.
- Assets: use
allowfor narrowly scoped local directories and verify that CSS, fonts and images are reachable. - Authentication: use renderer cookie or post options only for controlled values; never concatenate untrusted input into command arguments.
- JavaScript: keep it enabled only when required. Pages using modern ES6 APIs may fail because wkhtmltopdf is not fully compatible with them; polyfills can help in some applications.
7. Security checklist
- Do not enable
--enable-local-file-accessbroadly for untrusted HTML or JavaScript. Snappy documentation warns that local files or remote code execution may be exposed. - Prefer allow-listed asset directories over unrestricted filesystem access.
- Keep user-controlled URLs, cookies and headers out of shell strings; pass them through the bundle’s structured options.
- Run the renderer with a restricted OS user and writable temporary directory only.
- Validate destination URLs and block internal network ranges when rendering user-supplied URLs.
8. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Executable not found | Wrong binary path or missing package |
Run command -v wkhtmltopdf, copy the absolute path, and verify the PHP worker sees the same filesystem. |
| Permission denied | Binary or temporary directory is inaccessible | Grant execute permission to the binary and write permission to temporary_folder. |
| Process timed out | Slow remote assets, JavaScript or an undersized timeout | Inspect the URL independently, reduce unnecessary resources, add a bounded delay, or increase process_timeout to match your workload. |
| Blank PDF or image | Assets blocked, page requires authentication, or rendering starts before content appears | Check cookies and URLs, use allow for required local assets, and confirm the page works with the same renderer command. |
| Modern frontend fails | Incompatible ES6 API or JavaScript dependency | Provide compatible bundles or polyfills, disable JavaScript where possible, or use a renderer designed for the page. |
| Local images or fonts missing | Local file access is disabled or paths are outside allowed directories | Use a narrow allow path and ensure URLs resolve from the renderer’s process. |
| Configuration changes ignored | Cached Symfony container | Clear the environment cache and confirm the active environment loads the expected YAML file. |
9. Performance and reliability
- Reuse a stable renderer installation and keep temporary storage on a fast local disk.
- Reduce page weight, third-party requests and JavaScript work before increasing timeouts.
- Separate interactive HTTP requests from bulk document generation with a queue.
- Record renderer exit codes and stderr so failures can be diagnosed instead of retried blindly.
- Use deterministic HTML, pinned asset versions and explicit fonts when output must be reproducible.
- Test the exact PHP, Symfony, Snappy and renderer versions together. Package metadata changes over time; verify the registry and lockfile before publishing version-specific requirements.
10. When an API is simpler
KnpSnappyBundle is useful when your Symfony application must own the renderer process. If you only need a website screenshot, a hosted API removes binary installation, process management and browser setup.

Or skip the browser setup
ScreenshotNeo provides one GET request for a PNG, JPEG, WebP or PDF. See the API documentation for all options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
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(`HTTP ${res.status}`);
const bytes = new Uint8Array(await res.arrayBuffer());
ScreenshotNeo removes cookie banners, newsletter popups and chat widgets before capture. Bot checks, blank pages and failed loads are never billed, and response headers identify the page verdict and whether it was billed. Its MCP server lets Claude, Cursor and other MCP clients call take_screenshot, get_page_info and capture_pdf. The free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 shots.
Create a free ScreenshotNeo account.
FAQ
Can PDF and image rendering be configured separately?
Yes. Each section has its own enabled, binary and options values.
Where should renderer binaries live?
Anywhere the Symfony worker can execute them. Store the absolute path in an environment variable when paths differ by environment.
Is process_timeout a renderer timeout or an HTTP timeout?
It controls the bundle’s renderer process in seconds. Your web server, PHP-FPM and queue may impose separate limits.
Why does a page work in Chrome but fail in wkhtmltopdf?
wkhtmltopdf has different JavaScript and browser-engine support. Check ES6 compatibility, asset access and authentication using the installed binary itself.
Should I enable local file access?
Only when the input and allowed paths are controlled. Broad local-file access is unsafe for untrusted HTML or JavaScript.


