OG Images in PHP & Laravel

This guide covers OG images in PHP & Laravel with FastOG — sign an HMAC URL on your backend, drop it in a meta tag, and your blog posts get dynamic Open Graph images automatically. Credit-based with a free account (100 starter credits; create your API key in the dashboard); try the Free OG Image Tester to preview a template before you wire it up.

Complete guide to implementing HMAC-signed OG image URLs in PHP and Laravel.

Quick Start

1. Create the signer service

php
// app/Services/QuickogSigner.php
namespace App\Services;

class QuickogSigner
{
    public function generateSignedOgUrl(array $params, array $options = []): string
    {
        $baseUrl = $options['base_url'] ?? config('services.fastog.base_url', 'https://fastog.com/api/v1/og');
        $apiKey = $options['api_key'] ?? config('services.fastog.api_key');
        $hmacSecret = $options['hmac_secret'] ?? config('services.fastog.hmac_secret');

        if (!$apiKey || !$hmacSecret) {
            throw new \RuntimeException('Missing FASTOG_API_KEY or FASTOG_HMAC_SECRET');
        }

        $timestamp = time();
        $apiKey = $apiKey;

        // Build query params
        $query = $params;
        $query['key'] = $apiKey;
        $query['s'] = $this->sign($timestamp, $apiKey, $hmacSecret);
        $query['t'] = $timestamp;

        return $baseUrl . '?' . http_build_query($query);
    }

    private function sign(int $timestamp, string $apiKey, string $hmacSecret): string
    {
        $message = 'GET/api/v1/og' . $timestamp . $apiKey;
        return hash_hmac('sha256', $message, $hmacSecret);
    }
}

2. Configuration

php
// config/services.php
return [
    // ...
    'fastog' => [
        'api_key' => env('FASTOG_API_KEY'),
        'hmac_secret' => env('FASTOG_HMAC_SECRET'),
        'base_url' => env('FASTOG_BASE_URL', 'https://fastog.com/api/v1/og'),
    ],
];
bash
# .env
FASTOG_API_KEY=sk_your_full_api_key_here
FASTOG_HMAC_SECRET=your_64_char_hex_secret_here

3. Register as singleton

php
// app/Providers/AppServiceProvider.php
use App\Services\QuickogSigner;

public function register(): void
{
    $this->app->singleton(QuickogSigner::class);
}

Laravel Blade

blade
{{-- resources/views/blog/show.blade.php --}}
@extends('layouts.app')

@section('head')
    <meta property="og:image" content="{{ $ogImageUrl }}" />
    <meta property="og:title" content="{{ $post->title }}" />
    <meta property="og:description" content="{{ $post->excerpt }}" />
@endsection

@section('content')
    <article>
        <h1>{{ $post->title }}</h1>
        <p>{{ $post->excerpt }}</p>
    </article>
@endsection

Controller

php
// app/Http/Controllers/BlogController.php
namespace App\Http\Controllers;

use App\Services\QuickogSigner;
use App\Models\Post;

class BlogController extends Controller
{
    public function show(Post $post, QuickogSigner $signer)
    {
        $ogImageUrl = $signer->generateSignedOgUrl([
            'template' => 'blog',
            'title' => $post->title,
            'subtitle' => $post->excerpt,
            'author' => $post->author->name,
        ]);

        return view('blog.show', compact('post', 'ogImageUrl'));
    }
}

API Route

php
// routes/api.php
use App\Services\QuickogSigner;
use Illuminate\Http\Request;

Route::get('/og', function (Request $request, QuickogSigner $signer) {
    $validated = $request->validate([
        'title' => 'required|string',
        'template' => 'nullable|string',
    ]);

    $ogImageUrl = $signer->generateSignedOgUrl([
        'template' => $validated['template'] ?? 'default',
        'title' => $validated['title'],
    ]);

    return response()->json(['ogImageUrl' => $ogImageUrl]);
});

E-commerce Example

php
// app/Http/Controllers/ProductController.php
public function show(Product $product, QuickogSigner $signer)
{
    $ogImageUrl = $signer->generateSignedOgUrl([
        'template' => 'ecommerce',
        'title' => $product->name,
        'price' => $product->formatted_price,
        'image' => $product->image_url,
    ]);

    return view('products.show', compact('product', 'ogImageUrl'));
}

User Profile Example

php
// app/Http/Controllers/ProfileController.php
public function show(User $user, QuickogSigner $signer)
{
    $ogImageUrl = $signer->generateSignedOgUrl([
        'template' => 'profile',
        'name' => $user->name,
        'avatar' => $user->avatar_url,
        'bio' => $user->bio,
    ]);

    return view('profile.show', compact('user', 'ogImageUrl'));
}

Plain PHP (No Framework)

php
// lib/fastog-signer.php
function generateSignedOgUrl(array $params, array $options = []): string
{
    $baseUrl = $options['base_url'] ?? 'https://fastog.com/api/v1/og';
    $apiKey = $options['api_key'] ?? $_ENV['FASTOG_API_KEY'];
    $hmacSecret = $options['hmac_secret'] ?? $_ENV['FASTOG_HMAC_SECRET'];

    $timestamp = time();
    $apiKey = $apiKey;

    $message = 'GET/api/v1/og' . $timestamp . $apiKey;
    $signature = hash_hmac('sha256', $message, $hmacSecret);

    $query = array_merge($params, [
        'key' => $apiKey,
        's' => $signature,
        't' => $timestamp,
    ]);

    return $baseUrl . '?' . http_build_query($query);
}
php
// index.php
require_once 'lib/fastog-signer.php';

$ogUrl = generateSignedOgUrl([
    'template' => 'blog',
    'title' => 'My Blog Post',
]);
?>
<!DOCTYPE html>
<html>
<head>
    <meta property="og:image" content="<?= htmlspecialchars($ogUrl) ?>" />
    <meta property="og:title" content="My Blog Post" />
</head>
<body>
    <h1>My Blog Post</h1>
</body>
</html>

Common Pitfalls

Wrong: 'key' => $apiKey

Correct: 'key' => $apiKey

Wrong Message Format

Wrong: 'GET:/api/v1/og:' . $timestamp . ':' . $apiKey

Correct: 'GET/api/v1/og' . $timestamp . $apiKey

Exposing HMAC Secret

Never expose FASTOG_HMAC_SECRET in frontend JavaScript or public config files.

Full API Reference

Related

Try it free — no signup required

Preview and test dynamic OG images in seconds.

Open the Free OG Image Tester