Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

PHP has no built-in function that renders a webpage into an image. To screenshot a URL, PHP must control a browser engine such as Chrome or Chromium. The browser loads HTML, CSS, fonts, images, and JavaScript; your PHP code waits for the page to reach the state you need, captures either the viewport or the full page, and writes an image file.

The most direct PHP implementation uses chrome-php/chrome. Browsershot provides a shorter wrapper but requires Node.js and Puppeteer, while a Playwright PHP package exposes another browser-automation workflow. This guide shows runnable patterns, readiness and full-page decisions, deployment requirements, troubleshooting, and an API alternative.

Choose the PHP screenshot approach

Your choice is mainly a runtime decision, not a difference in image quality. All three library approaches ultimately drive a real browser.

Approach PHP call style Additional runtime Best fit
chrome-php/chrome Direct browser and page control Chrome or Chromium Applications needing explicit navigation, waits, formats, and browser options
Spatie Browsershot Browsershot::url(...)->save(...) Node.js, Puppeteer, and Chrome Projects that prefer a compact URL/HTML-to-image wrapper
Playwright PHP Launch Chromium, create a page, then call screenshot() Playwright browser installation Teams already using Playwright-style automation

Check each project’s current release, supported PHP and browser versions, operating-system requirements, and installation instructions before deploying. These details change independently of your PHP code.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Requirements before writing code

  • A PHP application with Composer.
  • A writable output directory.
  • Chrome or Chromium installed on the server, or an explicitly configured executable path.
  • Network access to the page being captured, unless you are rendering local HTML.
  • Enough memory and process limits for a browser. A browser is a separate process; a successful Composer install does not prove that the browser dependency is available.

The chrome-php/chrome README lists PHP 7.4–8.5 and Chrome/Chromium 65 or newer. Treat those as the documented range for that project and verify the current compatibility matrix when you install it. Its documentation also describes the CHROME_PATH environment variable and explicit executable selection when Chrome is not discoverable.

Method 1: chrome-php/chrome (direct PHP control)

Install the package

composer require chrome-php/chrome

Install Chrome or Chromium separately. In containers and CI, make the executable path and required system libraries part of the image rather than assuming a desktop installation exists.

Capture the visible viewport

This complete example starts a headless browser, opens a page, waits for navigation, saves a PNG, and closes the browser even if capture fails.

<?php

require __DIR__ . '/vendor/autoload.php';

use HeadlessChromiumBrowserFactory;

$browser = (new BrowserFactory())->createBrowser();

try {
    $page = $browser->createPage();
    $page->navigate('https://example.com')->waitForNavigation();
    $page->screenshot()->saveToFile(__DIR__ . '/screenshot.png');
} finally {
    $browser->close();
}

waitForNavigation() confirms that navigation reached its completion condition; it does not guarantee that a single-page application has fetched its data, that lazy images are decoded, or that web fonts have finished loading. Add an application-specific wait before the screenshot when those details matter.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Configure Chrome explicitly

If Chrome is not on the process PATH, configure the executable using the package’s supported factory options or its documented CHROME_PATH environment variable. Keep the path outside source code when it differs by environment. Run the same command as the web worker or queue user so permissions and sandbox behavior match production.

Choose PNG, JPEG, or WebP

The package documents PNG, JPEG, and WebP output. Use PNG for sharp text and UI elements, JPEG when photographic content and smaller files matter, and WebP when your consumers support it and you want a compact web asset. Image dimensions and device scale affect both visual detail and file size.

Capture the entire page

A normal screenshot is the current viewport. Full-page capture is a separate option or clipping mechanism, and should be enabled deliberately. The exact method depends on the package version; follow its current screenshot API rather than combining syntax from another library. A long page can create a very tall image, consume substantial memory, and exceed downstream upload limits.

Make the capture reliable

Wait for the state your page needs

  1. Wait for navigation.
  2. Wait for a selector that proves the main content exists, if your library supports selector waits.
  3. Wait for a known application event or a short delay when client-side rendering has no stable selector.
  4. Ensure lazy-loaded images are in view or otherwise triggered before a full-page capture.
  5. Capture only after fonts and critical assets have loaded if typography is part of the comparison.

“Navigation complete” is not the same as “the page is visually complete.” Define readiness from your own page: a product grid count, a dashboard root element, or an application-loaded flag is more dependable than an arbitrary long sleep.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Control viewport and device pixels

Set a viewport that matches the device or breakpoint you are documenting. CSS pixels determine layout; device scale influences raster resolution and file size. A high scale can make text sharper but increases memory and output size. Record the viewport and scale with the file if screenshots will be compared over time.

Use safe cleanup in workers

Always close the browser in a finally block. In queue workers, leaking browser processes eventually exhausts memory or process limits. For high volume, measure your own concurrency and recycle workers before they accumulate stale processes.

Method 2: Spatie Browsershot

Browsershot presents a concise PHP interface for URL or HTML input:

<?php

use SpatieBrowsershotBrowsershot;

Browsershot::url('https://example.com')
    ->save(__DIR__ . '/screenshot.png');

This convenience layer runs Puppeteer, which drives headless Chrome. Install and configure Node.js, Puppeteer, and Chrome according to the current Browsershot documentation. The project notes that its older Chrome CLI v2 route is no longer maintained, so do not build a new deployment around that legacy path. Browsershot is attractive when your team accepts the Node dependency and wants a short URL-to-image call; direct browser control is preferable when you need detailed lifecycle and readiness control.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Method 3: Playwright PHP

A Playwright PHP package documents the same fundamental sequence: launch headless Chromium, create a page, navigate, then save a screenshot.

<?php

// Namespace and installation details depend on the Playwright PHP package version.
$playwright = PlaywrightPlaywright::create();
$browser = $playwright->chromium()->launch(['headless' => true]);
$page = $browser->newPage();
$page->goto('https://example.com');
$page->screenshot(__DIR__ . '/screenshot.png');
$browser->close();

Use the package’s current namespace, installation command, browser-install command, and method signatures; do not copy APIs between Playwright, Browsershot, and chrome-php/chrome. Verify project maturity, supported PHP versions, and browser releases before making it a production dependency. Playwright’s screenshot API documents options such as full-page capture and scale, but those options must match the PHP binding version you install.

Viewport, element, and full-page decisions

Viewport screenshot

Use the default viewport when you need what a user sees without scrolling, such as a responsive breakpoint check or a support ticket.

Element or region screenshot

Capture a specific element when surrounding navigation, cookie notices, or browser chrome would make the image noisy. Locate the element with a stable selector and wait for it before capturing. A selector that changes between builds will produce an error or an empty result.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Full-page screenshot

Enable the library’s full-page option when documentation, archival, or visual-regression work requires the complete scrollable document. Test very tall pages separately: fixed headers, infinite scrolling, sticky elements, and lazy loading can produce results that differ from a single viewport.

Saving and serving the file safely

  • Resolve output paths from a controlled directory; never let an untrusted URL become a filesystem path.
  • Check that the process user can create and overwrite the destination.
  • Use unique names for concurrent jobs to prevent one capture from replacing another.
  • Validate the resulting file and MIME type before publishing it.
  • Keep secrets out of page URLs. If authentication is required, use the library’s supported cookies, headers, or authenticated browser context and protect the output.

Troubleshooting PHP screenshots

“Chrome executable not found”

Cause: Chrome is missing or not discoverable by the worker. Fix: install Chrome/Chromium, set the documented executable path or CHROME_PATH, and run a command as the same user that executes PHP.

The Composer package installs but capture fails immediately

Cause: PHP dependencies are present but browser binaries or Linux libraries are not. Fix: install the browser and OS packages in the deployment image, then test a minimal URL capture from the production runtime.

The screenshot is blank or missing application data

Cause: capture occurred before client-side rendering, an API request failed, or the target blocked the automated browser. Fix: inspect page logs and network access, wait for a content selector or application-ready signal, and verify the URL manually from the same host.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Images or fonts are missing

Cause: lazy loading, blocked cross-origin assets, or capture before resources finish. Fix: trigger lazy content, wait for the relevant assets, permit required outbound requests, and use a stable font-loading condition.

Full-page output is enormous

Cause: the document is genuinely tall or device scale is high. Fix: capture a viewport or selected elements, lower the scale, split long documents, or choose JPEG/WebP where appropriate.

Browser processes accumulate

Cause: an exception path skipped cleanup. Fix: close in finally, add worker timeouts, and monitor process and memory usage.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance, reliability, and cost considerations

Starting a browser is more expensive than writing a local image, so reuse must be balanced against isolation. A long-lived browser can reduce startup overhead, while a fresh context per job limits state leakage. Whichever model you choose, cap concurrency, set navigation and overall job timeouts, and record the target URL, viewport, format, and failure reason.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For predictable builds, pin PHP packages and browser versions, run a smoke capture in CI, and keep a known static test page. Network-dependent pages can change without a code deployment; visual comparisons should therefore record the capture time and readiness condition.

Or skip the browser setup

ScreenshotNeo is a hosted website screenshot API and MCP server. One GET request returns PNG, JPEG, WebP, or PDF, so PHP only has to make an HTTP request and save the response.

PHP call

<?php

$url = 'https://stripe.com';
$query = http_build_query([
    'access_key' => 'YOUR_API_KEY',
    'url' => $url,
]);

$data = file_get_contents(
    'https://api.screenshotneo.com/v1/shot?' . $query
);

if ($data === false) {
    throw new RuntimeException('Screenshot request failed');
}

file_put_contents(__DIR__ . '/shot.webp', $data);

See the ScreenshotNeo documentation for authentication and options. It accepts 63 options, including full-page capture with lazy images loaded, CSS-selector element capture, dark mode, device presets or custom viewports, retina scale, PDF settings, custom CSS and JavaScript, click-before-capture, selector or network-idle waits, request and resource blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, and a usage API. Parameter names used by other screenshot APIs also work, which can simplify migration.

Equivalent cURL, Python, and Node.js calls

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)
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}`);

ScreenshotNeo accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; yearly billing provides two months free. Create a free ScreenshotNeo account to try the API without a card.

Frequently Asked Questions

Can PHP take a screenshot without Chrome?

Not for a browser-rendered webpage using these methods. PHP needs a browser engine such as Chrome or Chromium, either installed with a PHP library or provided by a hosted screenshot service.

Should I use a viewport or full-page screenshot?

Use a viewport for what is visible without scrolling and explicitly enable full-page capture when the complete document is required.

Why does my screenshot show an old page?

Caching, application state, or a page that has not reached its ready condition can produce stale output. Set an appropriate cache policy, wait for a content-specific condition, and verify the target response from the capture host.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.