Subiect

Pornire rapidă — Generează prima ta imagine OG

Acest ghid de pornire rapidă FastOG îți pune în funcțiune prima imagine Open Graph dinamică în câteva minute. Înregistrarea este gratuită — apoi alege un plan Dynamic API pentru cota de randare. Îți creezi cheia API și secretul HMAC la cerere din dashboard. Fiecare randare este curată — fără filigran pe niciun plan, inclusiv testerul gratuit. Încearcă Testerul Gratuit de Imagini OG pentru a vedea pipeline-ul în acțiune, apoi semnează propriile URL-uri cu pașii de mai jos.

Cum funcționează — Arhitectură

FastOG este construit pentru imagini OG la cerere: site-ul tău semnează un URL la momentul randării paginii, crawler-ul îl preia, FastOG randează PNG-ul o singură dată, iar Cloudflare îl pune în cache, astfel încât fiecare cerere ulterioară este instantanee și gratuită.

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

Cele trei reguli care fac să „funcționeze pur și simplu"

  1. Semnează conținutul, nu ceasul. Semnătura acoperă fiecare parametru (titlu, template, …). Parametri identici produc întotdeauna același URL — asta face ca edge cache-ul să funcționeze: același conținut → același URL → cache hit pentru totdeauna.
  2. Imaginea este permanentă după prima randare. Semnătura controlează doar prima randare. Odată ce o imagine este randată, edge-ul FastOG o păstrează timp de un an, cheia fiind parametrii de conținut — cererile ulterioare cu aceiași parametri sunt servite din edge și nu ajung niciodată la renderer (nu se numără în cota ta). O semnătură invalidă pe conținut din cache returnează totuși PNG-ul din cache (răspunsul raportează ce nivel a răspuns: X-FastOG-Worker-Cache: HIT sau R2); doar un miss de cache primește 401.
  3. Singurul mod de eșec. Un URL modificat (cineva schimbă title/template) primește 401 la FastOG pe un cache miss — deci nu poate fi folosit niciodată pentru a-ți consuma cota. URL-urile legitime nu expiră niciodată: rămân valide până când rotești secretul HMAC al cheii.

1. Obține cheia API și secretul HMAC

  1. Mergi la FastOG Dashboard → API Keys

  2. Apasă pe „Create New API Key”

  3. Dă-i un nume (de exemplu, „Production Website”)

  4. Copiază ambele valori (sunt afișate o singură dată!):

    • API Key: sk_... (32 de caractere după prefix)
    • HMAC Secret: șir hexazecimal de 64 de caractere
  5. Stochează-le în mediul tău:

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. Implementarea funcției de semnare a URL-urilor

Pentru tutoriale complete cu mai multe framework-uri și modele, vezi:

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. Utilizare în blogul/twebsite-ul tău

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. Testează implementarea

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>"

Rezultat așteptat:

  • ✅ Semnătură validă → Returnează imaginea OG
  • ❌ Parametru modificat (ex. title schimbat) → {"code": "invalid_signature"}
  • ❌ Semnătură invalidă → {"code": "invalid_signature"}

5. Lista de verificare pentru securitate

  • [ ] Cheia secretă HMAC stocată în variabile de mediu (nu în cod)
  • [ ] Utilizarea HTTPS pentru toate cererile
  • [ ] Cheia secretă nu este niciodată expusă în JavaScript-ul din partea clientului
  • [ ] Monitorizarea încercărilor de semnătură eșuate

6. Gestionarea erorilor

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

Pașii următori

  1. Implementează semnarea în backend-ul site-ului tău (vezi tutorialele)
  2. Testează cu API-ul FastOG
  3. Monitorizează logurile pentru încercările de semnare eșuate
  4. Configurează alerte pentru eșecurile repetate

Pentru documentația completă, vezi Ghidul de semnătură HMAC.

Referință completă a API

Încearcă gratuit — nu este necesară înregistrarea

Previzualizează și testează imagini OG dinamice în câteva secunde.

Deschide Testerul gratuit de imagini OG

A BeCaSimulations site