DEV Community

Cover image for How to Take a Website Screenshot with PHP
Vitalii Holben
Vitalii Holben

Posted on • Originally published at screenshotrun.com

How to Take a Website Screenshot with PHP

I want to walk you through the full process of taking website screenshots from PHP today. Not theory, not a library overview -- the actual thing. Open a terminal, install dependencies, write code, run it, get a PNG file with a screenshot. The whole path from an empty folder to a working result.

Why would you even need this? Link previews for a directory site, automated OG images, screenshots for client reports, visual monitoring. Sooner or later a PHP project needs to turn a URL into an image.

The problem is that PHP can't render web pages on its own. There's no browser engine built in. So we'll need a helper -- headless Chrome through Puppeteer. It's a Node.js package, and yes, that means we need Node alongside PHP. Sounds like overkill, but you'll see it's not that bad.

Let's try it.

Installing Node.js

First things first -- we need Node.js. Puppeteer won't run without it.

If you're on macOS with Homebrew, one command:

brew install node
Enter fullscreen mode Exit fullscreen mode

On Ubuntu it's a bit longer:

curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
Enter fullscreen mode Exit fullscreen mode

Quick check that everything installed:

node -v
npm -v
Enter fullscreen mode Exit fullscreen mode


See version numbers? Good, moving on.

Creating the project and installing Puppeteer

Now we need a separate folder for our screenshot tool. I usually put it next to the PHP project, but it doesn't really matter where.

mkdir screenshot-tool
cd screenshot-tool
npm init -y
npm install puppeteer
Enter fullscreen mode Exit fullscreen mode

This will take a minute. Puppeteer downloads a full Chromium binary during installation -- somewhere between 170 and 400 megabytes depending on your OS. Yeah, it's a chunky package. That's the price of getting a real browser you can control from code.

If you're on Ubuntu, Chrome might fail to launch the first time -- it needs system libraries that aren't there on a fresh install:

sudo apt-get install -y libnss3 libatk1.0-0 libatk-bridge2.0-0 \
    libcups2 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 \
    libxrandr2 libgbm1 libpango-1.0-0 libcairo2 libasound2
Enter fullscreen mode Exit fullscreen mode

On macOS you can skip this step -- everything works out of the box.

Writing the screenshot script

Now for the fun part. Create a file called screenshot.js inside the screenshot-tool folder:

const puppeteer = require('puppeteer');

const url = process.argv[2];
const output = process.argv[3] || 'screenshot.png';

if (!url) {
    console.error('Usage: node screenshot.js <url> [output-file]');
    process.exit(1);
}

(async () => {
    const browser = await puppeteer.launch({
        headless: 'new',
        args: ['--no-sandbox', '--disable-setuid-sandbox'],
    });

    const page = await browser.newPage();
    await page.setViewport({ width: 1280, height: 800 });

    await page.goto(url, {
        waitUntil: 'networkidle2',
        timeout: 30000,
    });

    await page.screenshot({ path: output, fullPage: false });
    await browser.close();

    console.log(output);
})();
Enter fullscreen mode Exit fullscreen mode

What this does: takes a URL from the command line arguments, launches headless Chrome, opens the page, waits for it to load, takes a screenshot, saves it to a file. Then closes the browser.

Let's test it right away:

node screenshot.js https://screenshotrun.com test.png
Enter fullscreen mode Exit fullscreen mode

If everything worked, you'll see a new test.png file in the folder. Open it -- if you see a screenshot of the page, the script works.

Connecting it to PHP

The script works from the terminal. Now we need PHP to call it. The idea is simple: PHP runs our Node script through shell_exec() like any other console command, and picks up the resulting PNG from disk.


<?php

$url = 'https://screenshotrun.com';
$outputDir = __DIR__ . '/screenshots';
$filename = md5($url) . '.png';
$outputFile = $outputDir . '/' . $filename;

$scriptPath = __DIR__ . '/../screenshot-tool/screenshot.js';

$command = sprintf(
    'node %s %s %s 2>&1',
    escapeshellarg($scriptPath),
    escapeshellarg($url),
    escapeshellarg($outputFile)
);

echo "Running: {$command}\n";

$output = shell_exec($command);

if (file_exists($outputFile)) {
    echo "Done! Saved to: {$outputFile}\n";
    echo "File size: " . round(filesize($outputFile) / 1024) . " KB\n";
} else {
    echo "Something went wrong.\n";
    echo "Output: {$output}\n";
}
Enter fullscreen mode Exit fullscreen mode

Note the 2>&1 at the end of the command -- it redirects stderr to stdout. Without it, if Node throws an error, PHP simply won't see it and you'll be left guessing why nothing works.

Adding options

The basic version works, but let's make it more useful. Here's a wrapper function with viewport size and full-page support:

<?php

function takeScreenshot(
    string $url,
    string $outputDir,
    int $width = 1280,
    int $height = 800,
    bool $fullPage = false
): ?string {
    $filename = md5($url . $width . $height . ($fullPage ? '1' : '0')) . '.png';
    $outputFile = rtrim($outputDir, '/') . '/' . $filename;

    $scriptPath = __DIR__ . '/../screenshot-tool/screenshot.js';

    $command = sprintf(
        'node %s %s %s %d %d %s 2>&1',
        escapeshellarg($scriptPath),
        escapeshellarg($url),
        escapeshellarg($outputFile),
        $width,
        $height,
        $fullPage ? 'true' : 'false'
    );

    shell_exec($command);

    return file_exists($outputFile) ? $outputFile : null;
}

// Desktop screenshot
$file = takeScreenshot('https://screenshotrun.com', __DIR__ . '/screenshots');
echo $file ? "Desktop: {$file}\n" : "Failed\n";

// Mobile (iPhone-sized)
$file = takeScreenshot('https://screenshotrun.com', __DIR__ . '/screenshots', 375, 812);
echo $file ? "Mobile: {$file}\n" : "Failed\n";

// Full page with scrolling
$file = takeScreenshot('https://screenshotrun.com', __DIR__ . '/screenshots', 1280, 800, true);
echo $file ? "Full page: {$file}\n" : "Failed\n";
Enter fullscreen mode Exit fullscreen mode

Now you can take desktop, mobile, and full-page screenshots with one function call.

There's a simpler way

We went through the whole thing: Node.js, Puppeteer with Chromium, a JS script, shell_exec from PHP, a wrapper with options. It works -- but it's a lot of moving parts for one task.

You can get the same result with a single HTTP request. No Node.js, no Chromium, no two scripts in two languages:

<?php

$apiKey = 'YOUR_API_KEY';

$ch = curl_init('https://screenshotrun.com/api/v1/screenshots');
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS     => json_encode([
        'url'           => 'https://screenshotrun.com',
        'format'        => 'png',
        'block_cookies' => true,
    ]),
    CURLOPT_RETURNTRANSFER => true,
]);

$response = json_decode(curl_exec($ch), true);
curl_close($ch);

echo "Screenshot ID: " . $response['data']['id'];
Enter fullscreen mode Exit fullscreen mode

I built ScreenshotRun as a simpler alternative โ€” one HTTP request instead of managing Puppeteer. Free tier gives you 300 screenshots/month.

If you're using AI coding tools like Claude or Cursor, there's also an MCP server that lets your AI agent take screenshots directly.

Top comments (0)