Images OG en PHP et Laravel
Ce guide couvre les images OG en PHP et Laravel avec FastOG — signez une URL HMAC sur votre backend, insérez-la dans une balise meta, et vos articles de blog obtiennent automatiquement des images Open Graph dynamiques. Abonnement avec compte gratuit (choisissez un plan Dynamic API ; créez votre clé API dans le tableau de bord) ; essayez le testeur d'images OG gratuit pour prévisualiser un modèle avant de l'intégrer.
Guide complet pour implémenter des URLs d'images OG signées HMAC en PHP et Laravel.
Démarrage rapide
1. Créer le service de signature
// 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');
}
// Canonical query: every param except `s`, including `key`
$all = array_merge($params, ['key' => $apiKey]);
$pairs = [];
foreach ($all as $k => $v) {
$values = is_array($v) ? $v : [$v];
sort($values, SORT_STRING);
foreach ($values as $vv) {
$pairs[] = rawurlencode((string) $k) . '=' . rawurlencode((string) $vv);
}
}
sort($pairs, SORT_STRING);
$canonical = implode('&', $pairs);
$signature = hash_hmac('sha256', 'GET/api/v1/og' . "\n" . $canonical, $hmacSecret);
return $baseUrl . '?' . http_build_query(array_merge($all, ['s' => $signature]), '', '&', PHP_QUERY_RFC3986);
}
}
Pourquoi rawurlencode ? La requête canonique est encodée en pourcentage selon la RFC-3986 (les espaces deviennent %20, jamais +). rawurlencode correspond exactement à ce format — urlencode non (il produit +), ce qui casse la signature et renvoie invalid_signature. Passer PHP_QUERY_RFC3986 à http_build_query conserve l’URL finale dans le même encodage.
2. Configuration
// 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'),
],
];
# .env
FASTOG_API_KEY=sk_your_full_api_key_here
FASTOG_HMAC_SECRET=your_64_char_hex_secret_here
3. Enregistrer comme singleton
// app/Providers/AppServiceProvider.php
use App\Services\QuickogSigner;
public function register(): void
{
$this->app->singleton(QuickogSigner::class);
}
Laravel 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
Contrôleur
// 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'));
}
}
Route d’API
// 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]);
});
Exemple de commerce électronique
// 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'));
}
Exemple d’article de blog
// app/Http/Controllers/ProfileController.php
public function show(User $user, QuickogSigner $signer)
{
$ogImageUrl = $signer->generateSignedOgUrl([
'template' => 'blog',
'title' => $user->name,
'subtitle' => $user->bio,
'author' => $user->name,
]);
return view('profile.show', compact('user', 'ogImageUrl'));
}
PHP simple (sans framework)
// 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'];
$all = array_merge($params, ['key' => $apiKey]);
$pairs = [];
foreach ($all as $k => $v) {
$values = is_array($v) ? $v : [$v];
sort($values, SORT_STRING);
foreach ($values as $vv) {
$pairs[] = rawurlencode((string) $k) . '=' . rawurlencode((string) $vv);
}
}
sort($pairs, SORT_STRING);
$canonical = implode('&', $pairs);
$signature = hash_hmac('sha256', 'GET/api/v1/og' . "\n" . $canonical, $hmacSecret);
return $baseUrl . '?' . http_build_query(array_merge($all, ['s' => $signature]), '', '&', PHP_QUERY_RFC3986);
}
// 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>
Pièges courants
Signature d’un mauvais message
❌ Erreur : hash_hmac('sha256', 'GET/api/v1/og' . $canonical, $hmacSecret) — le séparateur de saut de ligne est absent.
✅ Correct : hash_hmac('sha256', 'GET/api/v1/og' . "\n" . $canonical, $hmacSecret) — méthode + chemin, un saut de ligne, puis la requête canonique.
Omission de key dans la signature
❌ Erreur : signer uniquement vos paramètres de contenu et ajouter key à l’URL ensuite.
✅ Correct : key est fusionné dans les paramètres avant la construction de la requête canonique, et apparaît dans l’URL finale.
Paramètres répétés (tableaux)
Passez les valeurs de tableau à generateSignedOgUrl et laissez http_build_query écrire l’URL : il émet des clés entre crochets (features[0]=a&features[1]=b) tandis que la signature canonique utilise des clés répétées simples (features=a&features=b). Ne construisez pas manuellement features=a&features=b dans l’URL — PHP décode les clés répétées en une valeur scalaire (la dernière gagne), donc le serveur reconstruit une requête canonique différente et rejette avec invalid_signature.
401 missing_signature / invalid_signature
missing_signature (401) signifie que key ou s manque dans l’URL. invalid_signature (401) signifie que la requête canonique ne correspond pas — vérifiez le saut de ligne dans le message signé, la même hmac_secret des deux côtés, rawurlencode (et non urlencode) pour chaque clé et valeur, et les clés entre crochets pour les paramètres répétés dans l’URL.
Exposition de la clé secrète HMAC
N’exposez jamais FASTOG_HMAC_SECRET dans le JavaScript frontal ni dans les fichiers de configuration publics.