DEV Community

Custodia-Admin
Custodia-Admin

Posted on • Originally published at pagebolt.dev

Browsershot Alternative: A Drop-in Replacement for PHP Screenshot Generation

Browsershot Alternative: A Drop-in Replacement for PHP Screenshot Generation

If you're a Laravel/PHP developer maintaining spatie/browsershot, you've felt the pain. 7,000+ GitHub stars. Zero updates since 2021. And every deployment, every new server, every shared hosting environment comes with the same problem: install headless Chrome without permissions.

On shared hosting? Forget it.

On AWS Lambda? You're adding 500MB to your function size just for a screenshot.

On PaaS platforms like Heroku or Platform.sh? System binary dependencies are a nightmare.

There's a simpler way.

The Problem: Why Browsershot Breaks in Production

Browsershot is a PHP wrapper around Puppeteer, which requires a headless Chrome binary. Here's what that means:

Shared Hosting (the most painful scenario):

# Your host doesn't have Chrome installed
# You request root access... denied
# You try to compile from source... permission denied
# You give up and ship a broken feature
Enter fullscreen mode Exit fullscreen mode

Serverless (AWS Lambda, Google Cloud Functions):

# Lambda layer for Chrome = 500MB+
# Increases cold start time significantly
# Costs more due to larger package size
# Still fragile — Chrome crashes in memory-constrained environments
Enter fullscreen mode Exit fullscreen mode

Docker/Containerized:

# Docker image bloats to 1GB+ with Chrome
# CI/CD builds take longer
# Registry storage costs increase
# Memory footprint is heavy
Enter fullscreen mode Exit fullscreen mode

The Browsershot package hasn't been updated since 2021. Security patches are lagging. And the core problem remains: you're managing system-level dependencies in application code.

The Before/After: Code Comparison

Before — Browsershot (with dependency hell):

<?php
use Spatie\Browsershot\Browsershot;

// First, you need to install Chrome on your system (good luck with that)
// $ apt-get install chromium-browser  # if you have sudo
// $ brew install chromium             # if you're on Mac
// etc...

// THEN you can use it:
Browsershot::url('https://example.com')
    ->screenshot()
    ->save($pathToImage);

// Want to add a header? Another dependency.
// Want to wait for JS? Another config.
// Want auth cookies? Another workaround.
// Want to scale? Deploy more servers with Chrome installed.
Enter fullscreen mode Exit fullscreen mode

Problems:

  • 500MB+ Chrome binary required on every server
  • Installation fails on restricted environments
  • No updates since 2021
  • Memory-heavy process
  • Scaling costs increase with Chrome bloat

After — PageBolt API (zero dependencies):

<?php
use GuzzleHttp\Client;

$client = new Client([
    'headers' => ['Authorization' => 'Bearer YOUR_API_KEY']
]);

$response = $client->post('https://api.pagebolt.dev/take_screenshot', [
    'json' => ['url' => 'https://example.com']
]);

$data = json_decode($response->getBody(), true);
$imageUrl = $data['imageUrl'];

// Save or stream the image
file_put_contents('screenshot.png', file_get_contents($imageUrl));
Enter fullscreen mode Exit fullscreen mode

Or with pure curl (no dependencies):

<?php
$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL => 'https://api.pagebolt.dev/take_screenshot',
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => ['Authorization: Bearer YOUR_API_KEY', 'Content-Type: application/json'],
    CURLOPT_POSTFIELDS => json_encode(['url' => 'https://example.com']),
    CURLOPT_RETURNTRANSFER => true
]);

$response = curl_exec($ch);
$data = json_decode($response, true);
echo $data['imageUrl']; // URL to your screenshot
curl_close($ch);
Enter fullscreen mode Exit fullscreen mode

Benefits:

  • Zero system dependencies
  • Works on any PHP environment (shared hosting, serverless, containers)
  • No Chrome installation required
  • Fast, lightweight HTTP requests
  • Scales instantly without managing infrastructure

Feature Comparison: What Actually Works

Feature Browsershot PageBolt API
System Dependencies ❌ Requires headless Chrome binary (500MB+) ✅ None — HTTP requests only
Shared Hosting ❌ Usually blocked by permissions ✅ Works everywhere
Serverless (Lambda) ⚠️ Heavy layer (500MB+), slow cold starts ✅ Lightweight, fast requests
Maintenance Status 🚨 Abandoned (2021) ✅ Active development
CSS/JS Support ⚠️ Older Chrome engine ✅ Modern Chromium rendering
Authentication ⚠️ Cookies, workaround-heavy ✅ Built-in auth support
Viewport Control ✅ Supported ✅ Full control
Custom Headers ⚠️ Complex setup ✅ Simple JSON parameter
Scaling ❌ Requires more servers with Chrome ✅ Scales instantly, no infrastructure
Cost (100 screenshots/month) $50-200 (server overhead) $1 (pay-per-request)

Real-World Cost Analysis

Scenario: Generate 5,000 screenshots/month for a SaaS app

Option 1: Self-Hosted Browsershot

  • Shared hosting: Can't run it. Upgrade to VPS ($50/month) minimum.
  • VPS ($50/month) + Chrome bloat + memory overhead
  • System admin time managing dependencies: 3 hrs/month
  • Labor cost: $300/month (3 hrs × $100/hr)
  • Total: $350/month

Option 2: AWS Lambda with Browsershot Layer

  • Lambda layer (500MB Chrome): Increases function size
  • Cold starts: 5-10 seconds vs. <100ms with API
  • Invocation time overhead
  • Layer version management complexity
  • Estimated cost: $100/month (storage + compute overhead)

Option 3: PageBolt API

  • 5,000 requests × $0.01 = $50/month
  • Zero infrastructure management
  • Instant scaling
  • No system dependencies
  • Total: $50/month

Savings with PageBolt: $300/month minimum. Plus 3 hours of engineering time freed.

Migration: Drop-in Replacement in 15 Minutes

Step 1: Create a helper class

<?php
namespace App\Services;

use GuzzleHttp\Client;

class ScreenshotService
{
    protected $client;
    protected $apiKey;

    public function __construct()
    {
        $this->apiKey = config('services.pagebolt.key');
        $this->client = new Client([
            'base_uri' => 'https://api.pagebolt.dev',
            'headers' => ['Authorization' => "Bearer {$this->apiKey}"]
        ]);
    }

    public function takeScreenshot($url, array $options = [])
    {
        $payload = array_merge(['url' => $url], $options);
        $response = $this->client->post('/take_screenshot', ['json' => $payload]);
        return json_decode($response->getBody(), true)['imageUrl'];
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Replace Browsershot calls

// Before
// Browsershot::url($url)->screenshot()->save($path);

// After
// $url = app(ScreenshotService::class)->takeScreenshot($url);
// file_put_contents($path, file_get_contents($url));
Enter fullscreen mode Exit fullscreen mode

Step 3: Update your .env

PAGEBOLT_API_KEY=your_key_here
Enter fullscreen mode Exit fullscreen mode

That's it. No system binary. No deployment pain. No scaling headaches.

When to Use What

Use PageBolt if:

  • You're on shared hosting or serverless
  • You generate 100+ screenshots/month
  • You want zero infrastructure management
  • You need modern CSS/JS rendering
  • You value maintainability and reliability

Use Browsershot if:

  • You're in a legacy monolith with ancient dependencies
  • You have full system admin control and time to manage Chrome
  • You generate <10 screenshots/month locally

(Spoiler: PageBolt wins for every real production scenario.)

Next Steps

  • Try PageBolt free — 100 requests/month, no credit card. Test with your actual URLs.
  • Copy the helper class above — works with Laravel, plain PHP, any framework.
  • Migrate one feature — replace one screenshot generation first. Measure the difference.

Your PHP/Laravel app deserves better than a 2021 dependency. Stop fighting Chrome binaries. Start generating screenshots that actually work, everywhere.


PageBolt makes screenshot generation simple, scalable, and dependency-free. Get started free →

Top comments (0)