Thema

Schnellstart – Ihr erstes OG-Bild generieren

Mit diesem FastOG-Schnellstart ist Ihr erstes dynamisches Open-Graph-Bild in wenigen Minuten live. Die Anmeldung ist kostenlos – wählen Sie dann einen Dynamic-API-Plan für das Render-Kontingent. Sie erstellen Ihren API-Schlüssel und das HMAC-Geheimnis bei Bedarf im Dashboard. Jedes Rendering ist sauber – ohne Wasserzeichen auf jedem Plan, kostenloser Tester inklusive. Probieren Sie den kostenlosen OG-Bild-Tester aus, um die Pipeline in Aktion zu sehen, und signieren Sie dann Ihre eigenen URLs mit den folgenden Schritten.

Funktionsweise — Architektur

FastOG wurde für On-Demand-OG-Bilder entwickelt: Ihre Website signiert eine URL zum Zeitpunkt des Seiten-Renderings, der Crawler ruft sie ab, FastOG rendert das PNG einmalig, und Cloudflare cached es, sodass jeder spätere Abruf sofort und kostenlos erfolgt.

sequenceDiagram
    participant You as You share a link
    participant Crawler as Social crawler
    participant Site as Your website
    participant FastOG as FastOG API
    participant Edge as Edge cache

    You->>Crawler: share a link to your post
    Crawler->>Site: GET the page without JavaScript
    Site->>Site: sign the og:image URL at render time
    Site-->>Crawler: HTML with signed og:image URL
    Crawler->>Edge: fetch the og:image URL
    Edge->>FastOG: cache miss, first time
    FastOG->>FastOG: verify the HMAC covers the exact params
    FastOG-->>Edge: render PNG, counts 1 toward monthly quota, cached for 1 year
    Edge-->>Crawler: PNG
    Crawler-->>You: image shown in the post preview
    Note over Edge, Crawler: identical params and URL mean a cache hit with no render

Die drei Regeln, die dafür sorgen, dass es „einfach funktioniert"

  1. Signieren Sie den Inhalt, nicht die Uhrzeit. Die Signatur deckt jeden Parameter ab (Titel, Vorlage, …). Identische Parameter erzeugen immer die identische URL — genau das macht den Edge-Cache funktionsfähig: gleicher Inhalt → gleiche URL → Cache-Treffer für immer.
  2. Das Bild ist nach dem ersten Rendern permanent. Die Signatur schützt nur das erste Rendern. Sobald ein Bild gerendert wurde, speichert FastOGs Edge es für ein Jahr, basierend auf den Inhalts-Parametern — spätere Anfragen mit denselben Parametern werden vom Edge bedient und erreichen den Renderer nie (sie zählen nicht gegen Ihr Kontingent). Eine ungültige Signatur bei gecachtem Inhalt liefert weiterhin das gecachte PNG (die Antwort meldet, welche Stufe geantwortet hat: X-FastOG-Worker-Cache: HIT oder R2); nur ein Cache-Fehltreffer wird mit 401 beantwortet.
  3. Der einzige Fehlermodus. Eine manipulierte URL (jemand ändert title/template) erhält 401 bei FastOG bei einem Cache-Fehltreffer — sie kann also niemals dazu verwendet werden, Ihr Kontingent zu verbrauchen. Legitime URLs laufen nie ab: Sie bleiben gültig, bis Sie das HMAC-Geheimnis des Schlüssels rotieren.

1. API-Schlüssel und HMAC-Geheimnis abrufen

  1. Gehen Sie zum FastOG-Dashboard → API-Schlüssel

  2. Klicken Sie auf „Neuen API-Schlüssel erstellen“

  3. Vergeben Sie einen Namen (z. B. „Produktionswebsite“)

  4. Kopieren Sie beide Werte (werden nur einmal angezeigt!):

    • API-Schlüssel: sk_... (32 Zeichen nach dem Präfix)
    • HMAC-Geheimnis: 64-stellige Hex-Zeichenfolge
  5. Speichern Sie sie in Ihrer Umgebung:

env
# Your website backend .env
FASTOG_API_KEY=sk_your_full_api_key
FASTOG_HMAC_SECRET=a1b2c3d4e5f6...  # 64 chars
FASTOG_API_URL=https://fastog.com/api/v1/og

2. Implementieren der URL-Signierungsfunktion

Für vollständige Tutorials mit mehreren Frameworks und Mustern siehe:

Node.js / Next.js

javascript
// lib/fastog-signer.js
import crypto from "crypto";

// RFC-3986 encode (encodeURIComponent leaves !'()* unencoded).
const enc = (s) =>
    encodeURIComponent(s).replace(
        /[!'()*]/g,
        (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase(),
    );

export function generateOgImageUrl(params) {
    const baseUrl = process.env.FASTOG_API_URL;
    const apiKey = process.env.FASTOG_API_KEY;
    const hmacSecret = process.env.FASTOG_HMAC_SECRET;

    const all = { ...params, key: apiKey };

    // Canonical: every param (except s), sorted, RFC-3986 encoded, joined with "&".
    const pairs = [];
    for (const [k, v] of Object.entries(all)) {
        for (const vv of Array.isArray(v) ? v : [v])
            pairs.push(`${enc(k)}=${enc(vv)}`);
    }
    pairs.sort();
    const canonical = pairs.join("&");

    // Content-signed: message = path + canonical query. No timestamp.
    const signature = crypto
        .createHmac("sha256", hmacSecret)
        .update(`GET/api/v1/og\n${canonical}`)
        .digest("hex");

    const url = new URL(baseUrl);
    for (const [k, v] of Object.entries(all)) {
        for (const vv of Array.isArray(v) ? v : [v])
            url.searchParams.append(k, vv);
    }
    url.searchParams.set("s", signature);
    return url.toString();
}

PHP (Laravel)

php
// app/Services/QuickogSigner.php
function generateOgImageUrl(array $params): string
{
    $baseUrl = config('services.fastog.api_url', 'https://fastog.com/api/v1/og');
    $apiKey = config('services.fastog.api_key');
    $hmacSecret = config('services.fastog.hmac_secret');

    $all = array_merge($params, ['key' => $apiKey]);

    // Canonical: every param except s, keys sorted (array values sorted too),
    // RFC-3986 encoded, joined with '&'. Content-signed — no timestamp.
    $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]));
}

Python (Django/FastAPI)

python
# utils/fastog.py
import hmac
import hashlib
import os
from urllib.parse import quote, urlencode

def _enc(s):
    return quote(str(s), safe='-._~')  # RFC-3986: only unreserved chars

def generate_og_image_url(params: dict) -> str:
    base_url = os.environ['FASTOG_API_URL']
    api_key = os.environ['FASTOG_API_KEY']
    hmac_secret = os.environ['FASTOG_HMAC_SECRET']

    all_params = {**params, 'key': api_key}

    # Canonical: every param except s, sorted, RFC-3986 encoded, joined with '&'.
    pairs = []
    for k, v in all_params.items():
        for vv in sorted(v if isinstance(v, list) else [v]):
            pairs.append(f"{_enc(k)}={_enc(vv)}")
    canonical = '&'.join(sorted(pairs))

    # Content-signed: message = path + canonical query. No timestamp.
    signature = hmac.new(
        hmac_secret.encode(),
        f"GET/api/v1/og\n{canonical}".encode(),
        hashlib.sha256,
    ).hexdigest()

    return f"{base_url}?{urlencode({**all_params, 's': signature})}"

3. Verwendung in Ihrem Blog/Ihrer Website

Next.js App Router

tsx
// app/blog/[slug]/page.tsx
import { generateOgImageUrl } from "@/lib/fastog-signer";

export async function generateMetadata({ params }) {
    const post = await getPost(params.slug);

    const ogImageUrl = generateOgImageUrl({
        template: "blog",
        title: post.title,
        subtitle: post.excerpt,
        author: post.author.name,
    });

    return {
        title: post.title,
        description: post.excerpt,
        openGraph: {
            images: [ogImageUrl],
        },
    };
}

Laravel Blade

blade
{{-- resources/views/blog/show.blade.php --}}
@php
    $ogImageUrl = generateOgImageUrl([
        'template' => 'blog',
        'title' => $post->title,
        'subtitle' => $post->excerpt,
        'author' => $post->author->name,
    ]);
@endphp

<head>
    <meta property="og:image" content="{{ $ogImageUrl }}" />
    <meta property="og:title" content="{{ $post->title }}" />
    <meta property="og:description" content="{{ $post->excerpt }}" />
</head>

Django Template

django
{# blog/post_detail.html #}
{% load fastog_tags %}

<head>
    <meta property="og:image" content="{% generate_og_image_url post %}" />
    <meta property="og:title" content="{{ post.title }}" />
    <meta property="og:description" content="{{ post.excerpt }}" />
</head>

4. Testen Sie Ihre Implementierung

bash
# Generate a test URL
node -e "
const { generateOgImageUrl } = require('./lib/fastog-signer');
console.log(generateOgImageUrl({ title: 'Test', template: 'default' }));
"

# Test with curl
curl "https://fastog.com/api/v1/og?title=Test&key=sk_quick_123abc&s=<sig>"

Erwartet:

  • ✅ Gültige Signatur → Gibt OG-Bild zurück
  • ❌ Manipulierter Parameter (z. B. title geändert) → {"code": "invalid_signature"}
  • ❌ Ungültige Signatur → {"code": "invalid_signature"}

5. Sicherheits-Checkliste

  • [ ] HMAC-Geheimnis in Umgebungsvariablen gespeichert (nicht im Code)
  • [ ] Verwendung von HTTPS für alle Anfragen
  • [ ] Geheimnis niemals im clientseitigen JavaScript offengelegt
  • [ ] Überwachung fehlgeschlagener Signaturversuche

6. Fehlerbehandlung

javascript
if (response.status === 401) {
    const error = await response.json();

    switch (error.code) {
        case "missing_signature":
            // URL is missing `key` or `s` — re-generate it with your signer.
            break;
        case "invalid_signature":
            // A param changed after signing, or the secret doesn't match.
            // Re-sign the URL after any content change; verify the secret on both sides.
            break;
    }
}

Nächste Schritte

  1. Implementieren Sie die Signierung in Ihrem Website-Backend (siehe die Tutorials)
  2. Testen Sie mit der FastOG-API
  3. Überwachen Sie die Logs auf fehlgeschlagene Signaturversuche
  4. Richten Sie Warnmeldungen für wiederholte Fehlschläge ein

Die vollständige Dokumentation finden Sie im HMAC-Signatur-Leitfaden.

Vollständige API-Referenz

Kostenlos testen – keine Anmeldung erforderlich

Vorschau und Test dynamischer OG-Bilder in Sekunden.

Kostenlosen OG-Bild-Tester öffnen

A BeCaSimulations site