How to Take Website Screenshots in PHP
PHP remains one of the most-used server-side languages, powering WordPress, Laravel, and Symfony. This guide covers three ways to capture website screenshots from PHP code.
Method 1: Browsershot (Puppeteer wrapper)
Browsershot is a popular Laravel/PHP package by Spatie that wraps Puppeteer. It requires Node.js and Puppeteer installed on the server.
Installation
composer require spatie/browsershot
npm install puppeteerYou also need Chrome or Chromium installed on the server.
Basic Screenshot
use Spatie\Browsershot\Browsershot;
Browsershot::url('https://example.com')
->windowSize(1280, 720)
->save('screenshot.png');Full-Page Screenshot
Browsershot::url('https://example.com')
->fullPage()
->save('full-page.png');Dark Mode
Browsershot::url('https://example.com')
->emulateMedia('screen')
->setOption('args', ['--force-dark-mode'])
->save('dark.png');PDF Export
Browsershot::url('https://example.com')
->format('A4')
->savePdf('page.pdf');Custom Viewport and Quality
Browsershot::url('https://example.com')
->windowSize(1440, 900)
->setScreenshotType('jpeg', 85)
->save('compressed.jpg');Use with Laravel
// In a Laravel controller
public function capture(Request $request)
{
$request->validate(['url' => 'required|url']);
$path = storage_path('app/screenshots/' . Str::random(16) . '.png');
Browsershot::url($request->input('url'))
->windowSize(1280, 720)
->waitUntilNetworkIdle()
->save($path);
return response()->download($path);
}Pros and Cons
Pros: Well-integrated with Laravel, fluent API, supports PDF, widely used in the PHP ecosystem.
Cons: Requires Node.js + Puppeteer + Chrome on the server, heavy resource usage (~250 MB RAM per capture), Chrome installation on shared hosting is often impossible, version conflicts between Node/Puppeteer/Chrome.
Method 2: SnapSharp API via cURL
The simplest approach — use PHP's built-in cURL to call the SnapSharp API. No additional dependencies beyond PHP itself.
Basic Screenshot
function screenshot(string $url, string $output, array $options = []): void
{
$params = array_merge([
'url' => $url,
'width' => 1280,
'height' => 720,
], $options);
$apiUrl = 'https://api.snapsharp.dev/v1/screenshot?' . http_build_query($params);
$ch = curl_init($apiUrl);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('SNAPSHARP_API_KEY'),
],
CURLOPT_TIMEOUT => 30,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new RuntimeException("Screenshot failed: HTTP $httpCode");
}
file_put_contents($output, $response);
}
screenshot('https://example.com', 'screenshot.png');Full-Page + Dark Mode
screenshot('https://example.com', 'dark-full.png', [
'full_page' => 'true',
'dark_mode' => 'true',
'format' => 'webp',
]);With Guzzle (Laravel / Symfony)
use GuzzleHttp\Client;
$client = new Client([
'base_uri' => 'https://api.snapsharp.dev/v1/',
'headers' => [
'Authorization' => 'Bearer ' . config('services.snapsharp.key'),
],
]);
$response = $client->get('screenshot', [
'query' => [
'url' => 'https://example.com',
'width' => 1280,
'height' => 720,
'full_page' => true,
],
]);
Storage::put('screenshots/example.png', $response->getBody()->getContents());Error Handling
function screenshotWithRetry(string $url, string $output, int $maxRetries = 3): void
{
for ($attempt = 0; $attempt < $maxRetries; $attempt++) {
$ch = curl_init('https://api.snapsharp.dev/v1/screenshot?' . http_build_query([
'url' => $url,
'width' => 1280,
]));
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('SNAPSHARP_API_KEY'),
],
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200) {
file_put_contents($output, $response);
return;
}
if ($httpCode === 429) {
sleep(pow(2, $attempt) * 5);
continue;
}
throw new RuntimeException("Screenshot failed: HTTP $httpCode");
}
throw new RuntimeException("Max retries exceeded");
}Method 3: SnapSharp PHP SDK
The official PHP SDK provides a typed, object-oriented interface with built-in retry logic.
Installation
composer require snapsharp/snapsharp-phpBasic Screenshot
use SnapSharp\SnapSharp;
$snap = new SnapSharp(getenv('SNAPSHARP_API_KEY'));
$image = $snap->screenshot('https://example.com', [
'width' => 1280,
'height' => 720,
]);
file_put_contents('screenshot.png', $image);Full-Page + Element Selector
$image = $snap->screenshot('https://example.com', [
'full_page' => true,
'selector' => '.main-content',
'dark_mode' => true,
]);PDF Export
$pdf = $snap->screenshot('https://example.com', [
'format' => 'pdf',
'pdf_format' => 'A4',
]);
file_put_contents('page.pdf', $pdf);Batch Capture in Laravel Queue
// App\Jobs\CaptureScreenshot.php
use SnapSharp\SnapSharp;
class CaptureScreenshot implements ShouldQueue
{
public function __construct(
private string $url,
private string $outputPath,
) {}
public function handle(): void
{
$snap = new SnapSharp(config('services.snapsharp.key'));
$image = $snap->screenshot($this->url, ['width' => 1280]);
Storage::put($this->outputPath, $image);
}
}
// Dispatch:
CaptureScreenshot::dispatch('https://example.com', 'screenshots/example.png');WordPress Integration
// In a WordPress plugin or theme
function snapsharp_capture_thumbnail(string $url): string
{
$api_key = get_option('snapsharp_api_key');
$response = wp_remote_get(
'https://api.snapsharp.dev/v1/screenshot?' . http_build_query([
'url' => $url,
'width' => 1200,
'height' => 630,
]),
['headers' => ['Authorization' => 'Bearer ' . $api_key]]
);
if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
return '';
}
$upload_dir = wp_upload_dir();
$filename = 'screenshot-' . md5($url) . '.png';
$filepath = $upload_dir['path'] . '/' . $filename;
file_put_contents($filepath, wp_remote_retrieve_body($response));
return $upload_dir['url'] . '/' . $filename;
}Pros and Cons
Pros: No local browser needed, works on shared hosting, typed SDK with retry logic, Laravel/WordPress friendly.
Cons: Network latency (~2–4s), API costs at scale (100 free/month), requires internet access.
Comparison Table
| Feature | Browsershot | cURL (raw API) | SnapSharp PHP SDK |
|---|---|---|---|
| Dependencies | Node.js + Puppeteer + Chrome | PHP cURL extension | Composer package |
| Shared hosting | Usually impossible | Works everywhere | Works everywhere |
| Full-page | Built-in | API parameter | SDK method |
| Dark mode | Chrome flags | API parameter | SDK method |
| Built-in | API parameter | SDK method | |
| Ad blocking | Manual | API parameter | SDK method |
| RAM usage | ~250 MB | Minimal | Minimal |
| Laravel integration | Native | Guzzle | SDK + Queue |
| WordPress | Requires Node.js | wp_remote_get | cURL fallback |
| Best for | Local/VPS with Node.js | Minimal dependency projects | Production PHP apps |
Which Should You Choose?
- Browsershot if you're on a VPS with Node.js already installed and want everything local.
- Raw cURL if you want zero Composer dependencies and full control.
- SnapSharp PHP SDK if you want a clean API, automatic retries, and don't want to install Chrome on your server.
For shared hosting (which rules out Browsershot entirely), the API approach is your only option — and it works beautifully.