Take Website Screenshots with PHP and Chrome on Shared Hosting
PHP can capture website screenshots with headless Chrome when your shared host permits the browser process. Check compatibility, run a PHP capture, and troubleshoot common limits.
Yes—PHP can take a website screenshot by controlling a Chrome or Chromium executable, but shared hosting only works if the account provides or permits that executable and its runtime requirements. The PHP library does not render pages itself: it launches and controls a browser. Before building around it, confirm the host allows browser processes, has compatible Chrome or Chromium, and provides enough runtime, memory, CPU, and writable temporary storage. Shared hosting does not guarantee any of those conditions.
This guide covers two local approaches—using a PHP library or invoking Chrome’s command line—and how to decide whether either can run on your account. If the host blocks browser execution, use a remote renderer or move the capture job to an environment that supports browser processes.
1. Check whether your shared-hosting account can run Chrome
Chrome Headless runs without a visible browser window. Current Chrome’s headless mode uses the regular Chrome browser code path; the older headless implementation became a separate chrome-headless-shell binary starting with Chrome 132. See Chrome’s Headless documentation and its Chrome 132 change notice.
For the PHP library route, the project currently lists PHP 7.4–8.5 and a Chrome/Chromium 65+ executable as requirements. Check the requirements for the exact package version you install, since project requirements can change. The library can use the CHROME_PATH environment variable or an executable path supplied to its factory. See the chrome-php/chrome project documentation.
| Check | What to establish |
|---|---|
| Browser binary | Is Chrome or Chromium installed, or can your account install a compatible binary in user space? What is its full path? |
| Process launch | Can the PHP worker launch external programs? Are functions such as exec() or proc_open() available in the web PHP configuration? |
| Runtime dependencies | Can the executable load its required shared libraries, and can it create a writable browser profile and temporary files? |
| Resource limits | What limits apply to memory, CPU, process count, script duration, disk space, and concurrent processes? |
| Host policy | Does the provider permit browser automation and the target-site traffic under its acceptable-use rules? |
| Job environment | Can the capture run from CLI or cron? Verify that its PHP version, configuration, binary path, and limits match the environment you intend to use. |
PHP documents exec() as a way to execute an external program. PHP configuration also includes disable_functions and resource controls such as memory_limit. Those facts do not certify your particular account; web and CLI PHP can differ. Check the account itself or ask the hosting provider before treating local Chrome as a viable production dependency. See the PHP exec() manual and PHP core configuration documentation.
2. Capture a screenshot through PHP with chrome-php/chrome
This route suits an application that needs to control navigation through PHP and save the resulting image. Install the package with Composer, make sure the browser executable is available to the PHP process, then use the following complete script. The output directory must already exist and be writable by the PHP user.
composer require chrome-php/chrome
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use HeadlessChromium\\BrowserFactory;
$url = 'https://example.com/';
$outputPath = __DIR__ . '/private-captures/example.png';
$chromePath = '/usr/bin/chromium'; // Replace with the path your host confirms.
if (!is_dir(dirname($outputPath)) || !is_writable(dirname($outputPath))) {
throw new RuntimeException('The screenshot directory must exist and be writable.');
}
$browserFactory = new BrowserFactory($chromePath);
$browser = $browserFactory->createBrowser();
try {
$page = $browser->createPage();
$page->navigate($url)->waitForNavigation();
$page->screenshot()->saveToFile($outputPath);
echo "Saved screenshot to {$outputPath}" . PHP_EOL;
} finally {
$browser->close();
}
If the binary is discoverable through CHROME_PATH, you can use new BrowserFactory() without the explicit path. The project also shows selecting a browser executable in the factory constructor. Keep the output in a directory outside the public web root where possible; use access controls if screenshots contain private data. Avoid letting a user choose arbitrary output paths or passing unchecked user input into shell commands.
Capture a region or a full page
The library documents screenshot output in PNG, JPEG, and WebP, region clipping, and full-page capture options. The exact option structure depends on the package version: consult that release’s README before using advanced options. A simple viewport screenshot is the example above; choose full-page capture when the image should include content below the initial viewport, and clipping when only a specific rectangle is needed.
For pages that populate content after initial navigation, a navigation wait may not mean every image, animation, or API request has finished. Use the library’s supported wait controls or a deliberate page readiness condition where available, and set an upper bound appropriate to your host’s execution limit. A fixed delay can help with a known page, but it is a poor substitute for a readiness condition when page speed varies.
3. Capture from PHP by invoking Chrome’s command line
The CLI route is useful for a basic viewport screenshot or a one-off capture. Chrome’s official command-line reference documents --screenshot, --window-size, and --timeout. A shell example is:
chrome --headless --screenshot --window-size=412,892 --timeout=5000 https://example.com/
Chrome saves screenshot.png in the current working directory. The timeout is a maximum wait in milliseconds before capture, even if the page is still loading. It does not guarantee that all page content has finished rendering. See the Chrome Headless command-line reference.
To call the executable from PHP, use a fixed, verified binary path, a controlled URL, and a unique output directory. The snippet below is deliberately for a fixed URL rather than user-submitted input:
<?php
declare(strict_types=1);
$chrome = '/usr/bin/chromium'; // Replace with the executable path verified on your host.
$url = 'https://example.com/';
$outputDir = __DIR__ . '/private-captures';
$outputPath = $outputDir . '/example.png';
if (!is_dir($outputDir) || !is_writable($outputDir)) {
throw new RuntimeException('The screenshot directory must exist and be writable.');
}
$args = [
$chrome,
'--headless',
'--screenshot=' . $outputPath,
'--window-size=1280,900',
'--timeout=5000',
$url,
];
$command = implode(' ', array_map('escapeshellarg', $args)) . ' 2>&1';
$output = [];
$exitCode = 0;
exec($command, $output, $exitCode);
if ($exitCode !== 0 || !is_file($outputPath) || filesize($outputPath) === 0) {
throw new RuntimeException("Chrome failed (exit {$exitCode}): " . implode("\\n", $output));
}
echo "Saved screenshot to {$outputPath}" . PHP_EOL;
Chrome documents the screenshot file in the current working directory for the basic flag form; the explicit output-path form in this PHP example is intended to keep the file location controlled. If the Chrome build on your host does not accept that form, run from the output directory and use --screenshot, then move the known output file after a successful exit.
PHP warns that user-supplied values passed to exec() must be escaped to prevent command injection. Escaping shell arguments is necessary when values vary, but it is not a substitute for URL validation, destination controls, and output-path controls. A public screenshot endpoint that accepts arbitrary URLs can also be abused to request internal network addresses. Restrict acceptable schemes and destinations, limit request frequency and execution time, and do not expose private cookies or headers to untrusted callers. See the PHP exec() documentation.
4. Choose between the PHP library and Chrome CLI
| Approach | Good fit | Considerations |
|---|---|---|
| PHP library controlling Chrome | Application logic needs to navigate and use browser controls from PHP; you may need region or full-page capture. | Requires Composer, a compatible executable, PHP process permissions, browser dependencies, and enough runtime. Check advanced option syntax against the installed release. |
| Chrome command line | Simple viewport captures, scripts, or scheduled jobs with a known Chrome executable. | PHP must be allowed to launch a process. It offers fewer high-level controls in the basic command, and shell argument handling requires care. |
| Remote screenshot API | The host cannot run Chrome or you prefer not to manage a browser binary on the shared account. | Compare dimensions, full-page behavior, timeouts, quotas, cost, privacy and retention, and reliability. The rendering happens at the provider, so consider what URLs, cookies, or other page data you send. |
Local rendering keeps the browser process under your hosting account, but you inherit its compatibility and resource constraints. A remote renderer moves browser execution elsewhere, but introduces a provider and network request. There is no universally cheaper, faster, or safer choice; the right one depends on your host and workload.
5. Troubleshoot common failures
| Symptom | Likely cause | What to check or change |
|---|---|---|
exec() is undefined, disabled, or returns a warning |
The relevant PHP configuration disables the process function, or the host blocks external process execution. | Check the web PHP SAPI’s configuration and ask the provider whether process launch is permitted. CLI access does not prove the web worker has the same settings. |
| “Chrome not found” or executable launch fails | The binary is absent, the path is wrong, or PHP cannot execute it. | Confirm the full path and executable permission under the same account and PHP environment that runs the capture. |
| Chrome starts locally in SSH but fails through the website | Web PHP may use a different user, environment, configuration, or limits from CLI PHP. | Compare the PHP SAPI, user permissions, environment variables, working directory, and provider restrictions. Test through the actual job environment. |
| Missing library or shared-object error | A runtime dependency required by the browser is unavailable to the account. | Capture stderr from the launch, identify the missing dependency, and ask whether the provider supports installing or supplying it. If not, use a browser-enabled environment or remote rendering. |
| Chrome exits without producing an image | Launch failed, the URL could not be loaded, the destination directory is unwritable, or the process was terminated. | Check the exit code and stderr, verify the output directory, and ensure the browser process has network access to the target. |
| Screenshot is blank or missing late-loading content | The page had not rendered the relevant content at capture time, or a script, bot check, or network dependency prevented it. | Use a bounded wait or page readiness condition, inspect the page behavior in a browser, and distinguish a genuine blank page from a timing issue. |
| Script times out or the host kills the process | Navigation or page scripts take too long, or the host’s execution limit is shorter than the capture. | Use a bounded timeout, reduce unnecessary work, avoid uncontrolled parallel captures, and compare the job’s timeout with the host limit. |
| “Old headless” flag error | The command uses the removed --headless=old mode on Chrome 132 or later. |
Use --headless for current Chrome, or use the standalone chrome-headless-shell only if you specifically need the old implementation. |
| Output cannot be opened from the website | The image was saved under a private path or a different working directory than expected. | Use an explicit server-side output path and serve it through a controlled download endpoint if it must be accessible. Do not make sensitive captures public by guessing a filename. |
6. Plan for performance, reliability, and cost
A local capture launches or uses a browser process, loads the target page, and writes an image. The work and resource use depend on the page and capture size; no single runtime or memory figure applies to every site or shared host. Large full-page captures, script-heavy pages, and parallel browser launches can put more pressure on limited account resources. Start with a small number of captures, bound waits, close the browser in a finally block, and keep temporary files under control.
For scheduled jobs, log the target, start time, exit code, elapsed time, and whether a non-empty output file was produced. Retry only failures that may be transient, with a limit and backoff; repeatedly launching Chrome after a permission or missing-library error will not fix the underlying issue. Avoid launching many captures at once unless your provider confirms the account can handle that concurrency.
Local Chrome has no per-capture vendor price in the supplied research, but the hosting account still has limits and operating costs. Remote services have their own plan prices, quotas, and data practices. Compare the current terms directly before committing, and consider where the target URL is rendered and whether it contains credentials or private data.
7. Or skip the browser setup
If your shared host cannot execute Chrome, ScreenshotNeo is a website screenshot API and MCP server. One GET request returns an image or PDF; the provider handles browser execution. Its API documentation lists the available request options.
<?php
$apiKey = 'YOUR_API_KEY';
$url = 'https://example.com';
$query = http_build_query([
'access_key' => $apiKey,
'url' => $url,
]);
$context = stream_context_create([
'http' => [
'timeout' => 90,
'ignore_errors' => true,
],
]);
$image = file_get_contents('https://api.screenshotneo.com/v1/shot?' . $query, false, $context);
if ($image === false) {
throw new RuntimeException('Screenshot request failed.');
}
file_put_contents(__DIR__ . '/shot.webp', $image);
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.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://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);
ScreenshotNeo accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each of those steps can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. The same features are available on every plan.
Sign up for ScreenshotNeo and get 1,000 screenshots a month free, with no card.
8. Frequently asked questions
Does a PHP screenshot library include Chrome?
No. chrome-php/chrome is a PHP interface for controlling a Chrome or Chromium executable. The host must make a compatible executable available to the PHP process.
Can I run headless Chrome without a desktop or display server?
Headless mode runs without a visible UI. Whether the particular binary and its dependencies work on your shared-hosting account is a separate compatibility question.
Can Chrome CLI capture an entire page?
The documented basic --screenshot example captures a screenshot with a chosen window size. For full-page capture, use a browser automation API that documents that capability or a screenshot service whose current documentation confirms it.
Should I use cron or a web request?
Use the execution method your provider supports. A scheduled CLI job can avoid keeping a web request open, but verify its PHP configuration, browser path, and resource limits independently from the website’s PHP worker.
Is a failed screenshot necessarily a PHP problem?
No. The failure can come from a missing browser dependency, host policy, navigation timeout, target-site behavior, or output permissions. Check the process exit information and browser output before changing PHP code.


