Θέμα

Εικόνες OG σε PHP & Laravel

Αυτός ο οδηγός καλύπτει τις εικόνες OG σε PHP & Laravel με το FastOG — υπογράψτε μια διεύθυνση URL HMAC στον διακομιστή σας, τοποθετήστε την σε μια ετικέτα meta, και οι αναρτήσεις του ιστολογίου σας θα αποκτούν αυτόματα δυναμικές εικόνες Open Graph. Η υπηρεσία λειτουργεί με συνδρομή και διαθέτει δωρεάν λογαριασμό (επιλέξτε ένα πρόγραμμα Dynamic API· δημιουργήστε το κλειδί API σας στον πίνακα ελέγχου)· δοκιμάστε το δωρεάν εργαλείο ελέγχου εικόνων OG για να προεπισκοπήσετε ένα πρότυπο πριν το συνδέσετε.

Πλήρης οδηγός για την υλοποίηση διευθύνσεων URL εικόνων OG με υπογραφή HMAC σε PHP και Laravel.

Γρήγορη εκκίνηση

1. Δημιουργία της υπηρεσίας υπογραφής

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');
        }

        // 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);
    }
}

Γιατί rawurlencode; Το κανονικό ερώτημα κωδικοποιείται με ποσοστιαία κωδικοποίηση σύμφωνα με το RFC-3986 (τα κενά γίνονται %20, ποτέ +). Το rawurlencode ταιριάζει ακριβώς με αυτό — το urlencode δεν ταιριάζει (εκπέμπει +), γεγονός που σπάει την υπογραφή και επιστρέφει invalid_signature. Η μεταβίβαση του PHP_QUERY_RFC3986 στο http_build_query διατηρεί την τελική διεύθυνση URL στην ίδια κωδικοποίηση.

2. Ρύθμιση παραμέτρων

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. Καταχώριση ως 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

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]);
});

Παράδειγμα Ηλεκτρονικού Εμπορίου

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'));
}

Παράδειγμα Άρθρου Ιστολογίου

php
// 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'));
}

Plain PHP (Χωρίς 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'];

    $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);
}
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>

Συνήθη Λάθη

Υπογραφή Λάθος Μηνύματος

❌ Λάθος: hash_hmac('sha256', 'GET/api/v1/og' . $canonical, $hmacSecret) — λείπει ο διαχωριστής νέας γραμμής. ✅ Σωστό: hash_hmac('sha256', 'GET/api/v1/og' . "\n" . $canonical, $hmacSecret) — μέθοδος + διαδρομή, νέα γραμμή και στη συνέχεια το κανονικό ερώτημα.

Παράλειψη του key από την Υπογραφή

❌ Λάθος: υπογραφή μόνο των παραμέτρων περιεχομένου και προσάρτηση του key στο URL εκ των υστέρων. ✅ Σωστό: το key συγχωνεύεται στις παραμέτρους πριν δημιουργηθεί το κανονικό ερώτημα και εμφανίζεται στο τελικό URL.

Επαναλαμβανόμενες Παράμετροι (Πίνακες)

Περάστε τιμές πίνακα στο generateSignedOgUrl και αφήστε το http_build_query να γράψει το URL: εκπέμπει κλειδιά με αγκύλες (features[0]=a&features[1]=b), ενώ το κανονικό ερώτημα υπογράφει απλά επαναλαμβανόμενα κλειδιά (features=a&features=b). Μην κατασκευάζετε χειροκίνητα το features=a&features=b στο URL — η PHP αποκωδικοποιεί τα επαναλαμβανόμενα κλειδιά σε βαθμωτή τιμή (κερδίζει η τελευταία τιμή), οπότε ο διακομιστής ανακατασκευάζει διαφορετικό κανονικό ερώτημα και απορρίπτει με invalid_signature.

401 missing_signature / invalid_signature

Το missing_signature (401) σημαίνει ότι το key ή το s λείπει από το URL. Το invalid_signature (401) σημαίνει ότι το κανονικό ερώτημα δεν ταιριάζει — επιβεβαιώστε τη νέα γραμμή στο υπογεγραμμένο μήνυμα, το ίδιο hmac_secret και στις δύο πλευρές, rawurlencode (όχι urlencode) για κάθε κλειδί και τιμή, καθώς και κλειδιά με αγκύλες για επαναλαμβανόμενες παραμέτρους στο URL.

Έκθεση του HMAC Secret

Μην εκθέτετε ποτέ το FASTOG_HMAC_SECRET σε JavaScript στο frontend ή σε δημόσια αρχεία διαμόρφωσης.

Πλήρης Αναφορά API

Δοκιμάστε το δωρεάν — δεν απαιτείται εγγραφή

Προεπισκόπηση και δοκιμή δυναμικών εικόνων OG σε δευτερόλεπτα.

Ανοίξτε τον Δωρεάν Ελεγκτή Εικόνων OG

A BeCaSimulations site