Gambar OG dalam PHP & Laravel
Panduan ini membahas gambar OG dalam PHP & Laravel dengan FastOG — tandatangani URL HMAC di backend Anda, letakkan di tag meta, dan postingan blog Anda akan mendapatkan gambar Open Graph dinamis secara otomatis. Berbasis langganan dengan akun gratis (pilih paket Dynamic API; buat kunci API Anda di dasbor); coba Penguji Gambar OG Gratis untuk melihat pratinjau template sebelum Anda menghubungkannya.
Panduan lengkap untuk mengimplementasikan URL gambar OG yang ditandatangani HMAC dalam PHP dan Laravel.
Mulai Cepat
1. Buat layanan penanda tangan
// 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);
}
}
Mengapa rawurlencode? Kueri kanonik di-encode persen sesuai RFC-3986 (spasi → %20, bukan +). rawurlencode cocok dengan ini secara tepat — urlencode tidak (ia menghasilkan +), yang merusak tanda tangan dan mengembalikan invalid_signature. Meneruskan PHP_QUERY_RFC3986 ke http_build_query menjaga URL akhir dalam encoding yang sama.
2. Konfigurasi
// 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. Daftarkan sebagai 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
Controller
// 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'));
}
}
Rute 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]);
});
Contoh E-commerce
// 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'));
}
Contoh Posting 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 Polos (Tanpa 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>
Kesalahan Umum
Menandatangani Pesan yang Salah
❌ Salah: hash_hmac('sha256', 'GET/api/v1/og' . $canonical, $hmacSecret) — melewatkan pemisah baris baru.
✅ Benar: hash_hmac('sha256', 'GET/api/v1/og' . "\n" . $canonical, $hmacSecret) — metode + jalur, baris baru, lalu query kanonik.
Mengabaikan key dari Tanda Tangan
❌ Salah: hanya menandatangani parameter konten Anda dan menambahkan key ke URL setelahnya.
✅ Benar: key digabungkan ke dalam parameter sebelum query kanonik dibuat, dan muncul di URL akhir.
Parameter Berulang (Array)
Kirim nilai array ke generateSignedOgUrl dan biarkan http_build_query menulis URL: ia menghasilkan kunci kurung siku (features[0]=a&features[1]=b) sementara kanonik menandatangani kunci berulang biasa (features=a&features=b). Jangan membuat features=a&features=b secara manual di URL — PHP mendekode kunci berulang menjadi skalar (nilai terakhir yang menang), sehingga server membangun kanonik yang berbeda dan menolak dengan invalid_signature.
401 missing_signature / invalid_signature
missing_signature (401) berarti key atau s tidak ada di URL. invalid_signature (401) berarti kanonik tidak cocok — pastikan baris baru dalam pesan yang ditandatangani, hmac_secret yang sama di kedua sisi, rawurlencode (bukan urlencode) untuk setiap kunci dan nilai, serta kunci kurung siku untuk parameter berulang di URL.
Mengekspos Rahasia HMAC
Jangan pernah mengekspos FASTOG_HMAC_SECRET di JavaScript frontend atau file konfigurasi publik.