Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
A black screenshot usually means one of three things: PHP ran a different browser than your SSH shell, the browser captured before the page painted, or a later ImageMagick step produced an empty or altered canvas. Treat the image as an untrusted result. Log the exact command, stderr, exit code, effective user, environment, output path and file size; then reproduce the smallest capture as the same account used by PHP-FPM or Apache.
Start with evidence, not browser flags
exec() only runs a command. It does not prove that Chrome rendered a page or that the resulting bytes are a valid image. A command can return an empty file, write somewhere other than you expect, or fail while your PHP code ignores stderr and the numeric status.
Record these values for every failed request:
- The fully expanded command (with secrets and user data redacted from application output).
- Standard output, standard error and the integer exit code.
- The PHP worker’s effective user,
PATH,HOME,DISPLAY, current directory and temporary directory. - Absolute input and output paths, file size, image dimensions and the first few pixel values.
Use a private log owned by the service account, rotate it, and never send raw commands or stderr to a browser. If a URL, filename or flag can be influenced by a user, allow-list it and use escapeshellarg() (or avoid the shell entirely); otherwise a screenshot endpoint can become command execution.
Reproduce the failure as the web worker
- Find the account running PHP-FPM or Apache (for example, the pool’s
usersetting or the process owner). - Create a private writable directory for that account, such as
/var/lib/myapp/shot-tmp, with restrictive permissions. - Switch to that account and run the renderer with absolute executable and output paths. Do not rely on an interactive shell’s aliases, profile files or custom
PATH. - Use a known-good public URL and a fixed viewport before adding authentication, scrolling or post-processing.
sudo -u www-data env -i HOME=/var/lib/www-data PATH=/usr/bin:/bin DISPLAY=
/usr/bin/google-chrome --headless --disable-gpu
--screenshot=/var/lib/myapp/shot-tmp/baseline.png
--window-size=412,892 https://developer.chrome.com/
Chrome documents the equivalent baseline as chrome --headless --screenshot --window-size=412,892 https://developer.chrome.com/; it writes screenshot.png in the current directory. An explicit output path removes ambiguity. Some Chrome builds require a non-root sandbox configuration; prefer a dedicated unprivileged worker rather than running a browser as root. If your distribution uses Chromium, replace the executable with its absolute path.
#1 Best Overall
- 14" diagonal, 1366x768 resolution, HD BrightView LED, Glossy NON-TOUCH Display
A diagnostic PHP wrapper that fails loudly
This example captures stderr, preserves the exit code, uses an absolute output path and rejects zero-byte results. Adapt the executable and temporary directory to your server.
<?php
declare(strict_types=1);
$url = 'https://developer.chrome.com/';
$outDir = '/var/lib/myapp/shot-tmp';
$outFile = $outDir . '/shot-' . bin2hex(random_bytes(8)) . '.png';
$chrome = '/usr/bin/google-chrome';
if (!is_dir($outDir) || !is_writable($outDir)) {
throw new RuntimeException('Output directory is missing or not writable');
}
$command = implode(' ', [
escapeshellarg($chrome),
'--headless',
'--disable-gpu',
'--screenshot=' . escapeshellarg($outFile),
'--window-size=412,892',
escapeshellarg($url),
]) . ' 2>&1';
$lines = [];
$status = 0;
$started = microtime(true);
exec($command, $lines, $status);
$elapsed = microtime(true) - $started;
$log = [
'user' => function_exists('posix_geteuid') ? posix_geteuid() : 'unknown',
'cwd' => getcwd(),
'path' => getenv('PATH') ?: '',
'home' => getenv('HOME') ?: '',
'display' => getenv('DISPLAY') ?: '',
'tmp' => sys_get_temp_dir(),
'output' => $outFile,
'status' => $status,
'elapsed_seconds' => $elapsed,
'output_lines' => $lines,
];
error_log(json_encode($log, JSON_UNESCAPED_SLASHES));
if ($status !== 0 || !is_file($outFile) || filesize($outFile) === 0) {
throw new RuntimeException('Screenshot command failed; inspect protected stderr log');
}
header('Content-Type: image/png');
readfile($outFile);
For production, add a time limit around the child process, clean up old files, and return a generic error to callers. Keep the detailed record in your server log.
Check the layers in order
1. Executable, PATH and working directory
SSH commonly loads a richer PATH, a different HOME and a different current directory than PHP-FPM. Run command -v google-chrome or command -v chromium in an administrative shell, then use that absolute path in PHP. Log getcwd(), sys_get_temp_dir() and getenv() values. A relative output such as screenshot.png may be written into the service manager’s directory, not your project.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Rank #2
- 1.1 GHz (boost up to 2.4GHz) Intel Celeron N5030 Quad-Core
- 4GB DDR4 System Memory; 128GB Solid State Drive
- 11.6" HD (1366 x 768) Multi-Touch Display
- Combo headphone/microphone jack - Noble Wedge Lock slot - HDMI; 2 USB 3.1 Gen 1
- Windows 11 Pro
2. Permissions and temporary files
The worker must be able to traverse every parent directory, create browser profiles and cache files, write the output, and read fonts and certificates. A directory that is writable by your login account can still reject www-data. Give the worker a dedicated directory rather than making a project tree world-writable. Check disk space and inodes as well as Unix permissions; a full filesystem can leave a truncated image.
3. Display and headless mode
A truly headless Chrome capture normally does not need an X server. If your command, wrapper or ImageMagick operation expects a display, an empty DISPLAY or an inaccessible X socket can produce a black canvas or a failure. Do not copy an SSH session’s DISPLAY into a service. Either use a supported headless mode or deliberately configure a virtual display and grant the service account access to it.
4. Navigation and first paint
A browser can exit successfully before CSS, fonts, images or JavaScript finish. Confirm that the server can resolve the hostname, reach every asset, validate certificates, use the required proxy and authenticate. Add a navigation wait or a delay only after the baseline works. For applications that render after JavaScript, wait for a selector that proves the content exists; a fixed sleep is less reliable and increases latency.
Rank #3
- 256 GB SSD of storage.
- Multitasking is easy with 16GB of RAM
- Equipped with a blazing fast Core i5 2.00 GHz processor.
5. Output and pixel validation
Check the PNG’s byte count and dimensions with an image tool or a library. A valid file with a uniform black pixel field is different from a zero-byte file, a missing file or an image whose alpha channel is transparent. Sample several pixels and inspect color channels before changing browser options. This prevents an ImageMagick color-conversion problem from being mistaken for a Chrome failure.
When ImageMagick is in the pipeline
ImageMagick may be the component that turns a valid browser image into a black one. Determine whether the selected operation expects an X server or uses the DISPLAY environment. Then read the active policy.xml; policy can restrict delegates and coders, filesystem paths, memory, disk space, pixel dimensions, image count and runtime. A denied coder or exhausted pixel cache can result in a missing or incomplete output.
identify -verbose /absolute/path/baseline.png | head -80
magick -list policy
magick /absolute/path/baseline.png -format '%wx%h %[channels]n' info:
Capture the exact policy error in your protected log. Do not disable policy globally to make one request pass. Permit only the formats and resources your application needs, and keep ImageMagick isolated from untrusted input.
Rank #4
- EFFORTLESS EVERYDAY PERFORMANCE: Powered by Intel Celeron N4020 processor and Windows 11 Home system, delivering reliable, low-power efficiency for daily tasks like document editing, email, online classes, and web browsing
- 15.6-INCH FULL HD DISPLAY: Enjoy immersive visuals on the 15.6" FHD (1920x1080) anti-glare screen with micro-edge bezels. Delivers clear details and comfortable viewing for long study sessions, working on spreadsheets, and video playback
- RESPONSIVE MULTITASKING & STORAGE: Built with 4GB LPDDR4 RAM and 128GB eMMC storage for smooth daily essential use. Expand your storage by up to 1TB via the integrated TF card slot to easily store movies, photos, and working files
- ADVANCED CONNECTIVITY: Outfitted with 2x Full-Featured Type-C ports for data transfer, fast charging, and dual-monitor output, alongside 2x USB 3.2 Gen1 ports and a 3.5mm audio jack for complete peripheral compatibility
- LIGHTWEIGHT & SILENT OPERATION: Slim and portable for effortless travel or commuting. Features a 1MP HD webcam for remote meetings, 38Wh battery with 45W Type-C fast charging, and a fanless silent design for peaceful work environments.
Add complexity one option at a time
Once the known-good URL produces a non-black image, introduce changes in this order:
- Set the required viewport and device scale.
- Enable full-page capture or a CSS clip.
- Wait for a selector, network idle state or a bounded delay.
- Verify web fonts, images and lazy-loaded content.
- Add cookies, authorization headers and a controlled user agent.
- Add clicks, custom JavaScript, request blocking and post-processing.
After each change, retain the exit code, stderr, dimensions and a small pixel sample. The first change that breaks the image identifies the failing layer.
Common symptoms and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| Works in SSH, black through PHP | Different user, PATH, HOME, working directory or permissions | Run the absolute command as the PHP worker and log its environment. |
| No file or zero-byte file | Ignored non-zero exit, unwritable directory, full disk or blocked browser startup | Capture stderr and status; test directory ownership, space and the executable directly. |
| Correct dimensions, uniformly black | Display assumption, early capture, transparent/incorrect channel conversion or page-level black background | Use headless mode, wait for content, inspect channels and compare the browser’s baseline PNG before ImageMagick. |
| Blank page with status 0 | Navigation or assets failed in the service network context | Test DNS, certificates, proxy, firewall and authentication as the worker; wait for a meaningful selector. |
| Only long pages fail | Lazy content, pixel-cache or dimension limits | Capture a viewport first, then full page; inspect ImageMagick policy and available memory. |
| Intermittent failures | Cold starts, racing temporary files, resource exhaustion or unbounded waits | Use unique paths, bounded timeouts, cleanup, concurrency limits and structured logs. |
Security, reliability and operating cost
- Allow-list destination hosts, schemes and browser flags. Escape every dynamic argument; never concatenate untrusted shell syntax.
- Use a dedicated unprivileged service account, private temporary directories and a browser sandbox compatible with that account.
- Set navigation and process timeouts, cap page dimensions, limit concurrent jobs and remove abandoned profiles.
- Keep stderr out of HTTP responses because it can contain URLs, headers or credentials. Rotate logs and redact cookies and authorization values.
- Expect local browsers to require dependency and font maintenance, consume CPU and memory during cold starts, and expose outbound-network and sandbox decisions you must operate.
A hosted browser API trades some local installation control for simpler deployment. Compare options by browser-version control, font and dependency installation, cold-start latency, stderr observability, sandbox model, network egress, JavaScript and full-page fidelity, and recurring cost. ImageMagick remains a separate post-processing risk: its format support and policy limits must fit your threat model.
Best Value
- WINDOWS 11 | STABLE PERFORMANCE: Powered by Intel Celeron N4020 processor and Windows 11 system, this laptop delivers stable performance for everyday computing tasks. It supports web browsing, online learning, document editing, email communication, and basic office work with optimized power efficiency, providing a practical and reliable experience for essential daily use for daily use.
- 15.6” FHD IPS DISPLAY: Features a 15.6-inch Full HD IPS display with narrow bezels, offering wider viewing angles and clearer image details compared to standard panels. The improved screen-to-body ratio enhances visual experience for study, reading, document work, and video playback, making it suitable for both productivity and entertainment use.
- 4GB DDR4 + 128GB eMMC STORAGE: Equipped with 4GB DDR4 memory and 128GB eMMC storage for everyday basics such as browsing, documents, email, and online learning platforms. The built-in TF card slot supports storage expansion up to 1TB, giving you more flexibility for files, photos, videos, and daily documents. TF card not included.
- CONNECTIVITY & PORTS: Includes 1× TF card slot, 2× USB 3.2 Gen1 ports, and 2× full-featured Type-C ports (USB 3.2 Gen1). The Type-C ports support data transfer, charging, and video output, enabling flexible connection with external devices such as monitors, storage, and peripherals for daily work and study use.
- LIGHTWEIGHT DESIGN | ONLINE COMMUNICATION: Designed with a slim, portable profile, this laptop is easy to carry for school, commuting, and travel. A built-in 1MP front camera supports online classes, video meetings, remote communication, and everyday conferencing. The 3300mAh battery works with the low-power system design to support practical daily use, while thermal optimization helps maintain quieter operation during extended tasks.
Or skip the browser setup
ScreenshotNeo is a hosted screenshot API and MCP server. It removes cookie-consent banners, newsletter popups and chat widgets before capture; bot checks, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP tools—take_screenshot, get_page_info and capture_pdf—work with Claude, Cursor and other MCP clients.
One GET request returns a PNG, JPEG, WebP or PDF:
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()));
See the complete parameter list and response behavior in the ScreenshotNeo documentation. Options include full-page lazy-image loading, CSS-element capture, dark mode, 12 device presets or custom viewports, retina scale, PDF paper and page controls, HTML/CSS input, custom JavaScript, clicks, selector waits, network-idle waits, blocking rules, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous webhooks, 100-URL bulk calls, usage data and an OpenAPI specification. Existing integrations can use the parameter names common to other screenshot APIs.
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; yearly billing provides two months free, and every feature is available on every plan. Create a free ScreenshotNeo account and test the capture without installing Chrome or configuring a server display.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →FAQ
Should I set DISPLAY=:0 in PHP?
Only when the specific renderer requires an X server that the worker can access. A headless Chrome command should normally avoid that dependency; setting an arbitrary display can hide the real permission problem.
Why does a black image have a successful exit code?
Process success means the program completed, not that the page contained the pixels you wanted. Validate dimensions, channels and representative pixels, then inspect the page and post-processing separately.
Is a longer sleep the right fix?
It can mask a race, but a selector or network-idle condition tied to the page is more deterministic. Keep any delay bounded and verify that required fonts and images actually loaded.
Can I make ImageMagick ignore its policy?
Do not weaken policy globally. Identify the denied coder, delegate or resource, then make the narrowest reviewed change or remove the unnecessary operation.
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 errorsQuick 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.

