Ämne

Snabbstart — Skapa din första OG-bild

Den här snabbstarten för FastOG får din första dynamiska Open Graph-bild live på några minuter. Att registrera sig är gratis — välj sedan en Dynamic API-plan för renderingskvot. Du skapar din API-nyckel och HMAC-hemlighet på begäran i instrumentpanelen. Varje rendering är ren — ingen vattenstämpel på någon plan, gratis testare ingår. Prova den kostnadsfria OG-bildtestaren för att se pipelinen i aktion, och signera sedan dina egna URL:er med stegen nedan.

Så här fungerar det — Arkitektur

FastOG är byggt för on-demand OG-bilder: din webbplats signerar en URL vid sidans renderingstidpunkt, crawler hämtar den, FastOG renderar PNG-filen en gång, och Cloudflare cachar den så att varje senare hämtning är omedelbar och gratis.

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

De tre reglerna som gör att det "bara fungerar"

  1. Signera innehållet, inte klockan. Signaturen täcker varje parameter (titel, mall, …). Identiska parametrar ger alltid identisk URL — det är det som får edge-cachen att fungera: samma innehåll → samma URL → cacheträff för alltid.
  2. Bilden är permanent efter första renderingen. Signaturen styr bara den första renderingen. När en bild väl är renderad behåller FastOG:s edge den i ett år, nycklad på innehållsparametrarna — senare förfrågningar med samma parametrar serveras från edge och når aldrig renderaren (räknas inte mot din kvot). En ogiltig signatur på cachat innehåll returnerar fortfarande den cachade PNG-filen (svaret rapporterar vilken nivå som svarade: X-FastOG-Worker-Cache: HIT eller R2); bara en cache miss får 401.
  3. Det enda feltillståndet. En manipulerad URL (någon ändrar title/template) får 401 från FastOG vid en cache-miss — så den kan aldrig användas för att förbruka din kvot. Legitima URL:er går aldrig ut: de förblir giltiga tills du roterar nyckelns HMAC-hemlighet.

1. Hämta din API-nyckel och HMAC-hemlighet

  1. Gå till FastOG-instrumentpanelen → API-nycklar

  2. Klicka på "Skapa ny API-nyckel"

  3. Ge den ett namn (t.ex. "Produktionswebbplats")

  4. Kopiera båda värdena (visas endast en gång!):

    • API-nyckel: sk_... (32 tecken efter prefix)
    • HMAC-hemlighet: 64-teckens hexadecimal sträng
  5. Lagra dem i din miljö:

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. Implementera URL-signeringsfunktionen

För fullständiga handledningar med flera ramverk och mönster, se:

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. Användning i din blogg/webbplats

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. Testa din implementation

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

Förväntat:

  • ✅ Giltig signatur → Returnerar OG-bild
  • ❌ Ändrad parameter (t.ex. title ändrad) → {"code": "invalid_signature"}
  • ❌ Ogiltig signatur → {"code": "invalid_signature"}

5. Säkerhetschecklista

  • [ ] HMAC-hemlighet lagrad i miljövariabler (inte i kod)
  • [ ] Använda HTTPS för alla förfrågningar
  • [ ] Hemligheten exponeras aldrig i JavaScript på klientsidan
  • [ ] Övervakning av misslyckade signaturförsök

6. Hantera fel

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ästa steg

  1. Implementera signering i din webbplats backend (se handledningarna)
  2. Testa med FastOG API
  3. Övervaka loggar för misslyckade signaturförsök
  4. Konfigurera aviseringar för upprepade misslyckanden

För fullständig dokumentation, se HMAC-signaturguiden.

Fullständig API-referens

Prova gratis – ingen registrering krävs

Förhandsgranska och testa dynamiska OG-bilder på några sekunder.

Öppna gratis OG-bildtestaren

A BeCaSimulations site