ScreenshotNeo

BlogHow-to

How to Capture a Website Screenshot on a Linux PHP Server

Capture reliable website screenshots from a Linux PHP server with Chrome, Chromium, full-page options, troubleshooting, and a hosted API alternative.

By the ScreenshotNeo team1 October 20268 min read

To capture a website screenshot from a Linux PHP server, run a headless Chrome or Chromium process and control it from PHP. The most direct documented PHP route is the chrome-php/chrome Composer package: launch the browser, navigate to the URL, wait for navigation, capture the page, save the image, and always close the browser.

The package documentation states support for PHP 7.4–8.5 and Chrome or Chromium 65+. Confirm those requirements against the package release you deploy. Read the chrome-php/chrome documentation.

1. Install PHP dependencies and a browser

Your server needs both the PHP library and an executable Chrome or Chromium browser. Install Chrome or Chromium using your Linux distribution’s package documentation, then verify that the executable can run under the same user as PHP.

composer require chrome-php/chrome

# Confirm that a browser executable is available.
which google-chrome || which chromium || which chromium-browser

chrome-php/chrome checks the CHROME_PATH environment variable and otherwise attempts to locate a browser or use chrome. Set CHROME_PATH when your distribution installs Chromium under a different name or path.

export CHROME_PATH=/usr/bin/chromium

You can also pass an explicit executable name or path to BrowserFactory. Keep the browser installed in the deployment image, and make sure the PHP process can execute it and write to the destination directory.

2. Capture a basic screenshot in PHP

This complete example opens a URL, waits for navigation, writes a PNG, and closes Chrome even when an exception occurs.

<?php

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

use HeadlessChromium\\BrowserFactory;

$url = $argv[1] ?? 'https://example.com';
$output = $argv[2] ?? __DIR__ . '/screenshot.png';

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

try {
    $page = $browser->createPage();
    $page->navigate($url)->waitForNavigation();
    $page->screenshot()->saveToFile($output);
    echo "Saved {$output}\\n";
} finally {
    $browser->close();
}

Run it from your application directory:

php capture.php https://example.com /tmp/example.png

The screenshot API supports PNG, JPEG, and WebP. PNG is the documented default. Quality applies to JPEG and WebP output. Use an output path that the PHP worker can write and that your deployment or CI system preserves.

3. Choose viewport, full-page, or clipped capture

Viewport screenshots

A normal screenshot records the content visible in the current browser viewport. Use this for monitoring a fixed desktop or mobile layout.

Full-page screenshots

For pages where content below the fold matters, request the page’s full-page clip and enable capture beyond the viewport:

$page = $browser->createPage();
$page->navigate('https://example.com/docs')->waitForNavigation();

$clip = $page->getFullPageClip();
$page->screenshot([
    'captureBeyondViewport' => true,
    'clip' => $clip,
])->saveToFile(__DIR__ . '/docs-full.png');

Very long pages can produce large images and consume substantial memory. Consider splitting long pages, using a viewport capture, or capturing only the region that matters.

Element or rectangular captures

Use an element or clipped region when the deliverable is a component such as a chart, invoice, product card, or report panel. The library documents rectangular clipping; calculate the target rectangle after the page reaches the required state, then pass that rectangle as the screenshot clip. Keep the selector and clipping logic tied to stable page markup.

4. Make page readiness explicit

waitForNavigation() confirms navigation progress, but a client-rendered application may still be loading data, fonts, images, or widgets. Before capturing, wait for a condition that represents the state you need: an expected heading, chart, table, or application marker.

  • Use a stable selector rather than a random animation or timestamp.
  • Wait for lazy-loaded content before a full-page capture.
  • Disable or wait for animations when pixel consistency matters.
  • Use a fixed test account and deterministic data for visual comparisons.
  • Set navigation and communication timeouts appropriate to your application.

Playwright’s PHP screenshot guidance also recommends making the desired state explicit and checking meaningful visibility conditions. Screenshots show one moment; retain a trace or another interaction record when the sequence of actions matters. See the Playwright PHP screenshot guidance.

5. Configure the browser for Linux deployment

Setting When to use it
Executable path Set CHROME_PATH or pass an explicit executable when Chrome is not discoverable as chrome.
Headless mode Use headless operation on servers without a desktop session.
Viewport and window size Fix these values when comparing screenshots or reproducing a bug.
Startup and communication timeouts Increase them for slow pages or constrained hosts; keep an upper bound so failed pages do not hang workers.
Proxy Configure a proxy when outbound access requires one.
noSandbox The project documents this option as useful in a Docker container. Treat it as a deployment decision that requires your own container and security review.

A persistent-browser pattern can reuse one browser process across scripts. The documentation describes synchronous and asynchronous use, but the reviewed material does not provide a throughput or resource benchmark. Measure your own workload before selecting a worker model.

6. A production-oriented PHP wrapper

<?php

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

use HeadlessChromium\\BrowserFactory;

function captureScreenshot(string $url, string $output): void
{
    if (!filter_var($url, FILTER_VALIDATE_URL)) {
        throw new InvalidArgumentException('Invalid URL');
    }

    $factory = new BrowserFactory();
    $browser = $factory->createBrowser([
        'headless' => true,
    ]);

    try {
        $page = $browser->createPage();
        $page->navigate($url)->waitForNavigation();
        $page->screenshot()->saveToFile($output);
    } finally {
        $browser->close();
    }
}

captureScreenshot(
    'https://example.com',
    __DIR__ . '/var/example.png'
);

Validate and authorize destination URLs in your application. A screenshot endpoint that accepts arbitrary URLs can become a server-side request mechanism. Restrict destinations, outbound network access, credentials, and stored artifacts according to your application’s threat model.

7. Troubleshooting common errors

Symptom Likely cause Fix
Browser executable not found Chrome is missing or installed under another name. Install Chrome or Chromium, set CHROME_PATH, or pass the executable explicitly.
Permission denied starting Chrome The PHP user cannot execute the browser or access its temporary directory. Run the process as the deployment user, fix executable and temp-directory permissions, and avoid writing screenshots to protected paths.
Navigation timeout The host is slow, unreachable, blocked by a proxy, or waiting on an application request. Check outbound connectivity and proxy settings, increase the documented timeout within a bounded limit, and wait for a page-specific condition.
Blank or incomplete image Capture happened before client-side rendering, lazy loading, fonts, or images completed. Wait for the required selector or state; for long pages use explicit full-page settings.
Full page is cut off The full-page clip or captureBeyondViewport option was omitted. Call getFullPageClip() and pass the clip with captureBeyondViewport => true.
Different pixels between runs Viewport, fonts, browser versions, animations, data, or rendering hosts differ. Pin those inputs, disable animations where possible, and compare in a controlled environment.
Large memory use A very long page or high-resolution image is being rendered. Capture a viewport or smaller regions, split the page, and limit concurrent browser pages.
Artifacts contain secrets or personal data The page rendered authenticated or private content. Redact or restrict captures, set retention limits, and do not expose output paths publicly.

8. PHP alternatives

Playwright PHP provides documented viewport, full-page, and element screenshots. Its examples list PHP 8.2+ and Node.js 20+ prerequisites and include installing Chromium. This is a reasonable choice for teams already standardizing on Playwright, but it adds the documented Node.js runtime requirement. Verify prerequisites against the current release. Read the Playwright PHP documentation.

Puppeteer is a Node.js library rather than a direct PHP API. It is useful when a separate Node worker or service is acceptable; it is not a drop-in PHP package. Chrome’s documentation identifies Puppeteer as a Node library for browser control and screenshots. See Chrome’s Puppeteer documentation.

9. Or skip the browser setup

ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. It handles the browser layer and removes cookie or consent banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and each response identifies the result with X-Page-Verdict and X-Billed headers.

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,
)
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 body = Buffer.from(await res.arrayBuffer());
require('fs').writeFileSync('shot.webp', body);

ScreenshotNeo also supports full-page capture, CSS-selector element capture, device presets, custom viewports, retina scale, PDF output, custom CSS and JavaScript, clicks, selector or network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, selectable cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.

There is a free plan with 1,000 screenshots per month and no card. Paid plans start at $5 for 3,000 screenshots; every feature is available on every plan. Read the ScreenshotNeo API documentation, then create a free account.

10. Performance, reliability, and cost notes

  • Launching a browser for every request adds startup work. A persistent browser can reduce repeated startup overhead, but benchmark your own pages and concurrency.
  • Full-page captures and high-resolution output use more memory than viewport captures.
  • Readiness waits improve correctness but increase latency. Use a meaningful selector or state rather than an arbitrary long delay.
  • Pin browser, PHP, and library versions when visual consistency matters, and recheck compatibility before upgrades.
  • Do not claim reliability or throughput from documentation alone; measure representative URLs, failure rates, memory, and queue time in your environment.
  • Keep screenshot retention and access controls proportional to the sensitivity of the pages being captured.

FAQ

Can PHP capture a website without Chrome or Chromium?

Not for modern browser rendering with the documented approach. The PHP library controls a Chrome or Chromium executable, so the browser must be installed on the Linux host or supplied by your deployment image.

Should I use a full-page screenshot for every page?

No. Use a viewport for the visible layout, full-page mode when below-the-fold content matters, and a clipped or element region for a focused component.

Why does a screenshot miss content that appears in a normal browser?

Navigation may have completed before client-side content, lazy images, fonts, or API data finished rendering. Wait for the specific state your capture requires.

Is Puppeteer a PHP library?

No. Puppeteer is a Node.js library. Use it through a separate Node worker or choose a PHP library such as chrome-php/chrome or Playwright PHP.

Are screenshots evidence that an interaction worked?

A screenshot records one visual state. Keep a trace or another interaction record when you must prove the sequence that produced it.