Emne

Hurtigstart — Generer ditt første OG-bilde

Denne FastOG-hurtigstarten får ditt første dynamiske Open Graph-bilde (OG-bilde) publisert i løpet av minutter. Registrering er gratis — velg deretter en Dynamic API-plan for gjengivelseskvote. Du oppretter API-nøkkelen og HMAC-hemmeligheten din etter behov i dashbordet. Hver gjengivelse er ren — ingen vannmerke på noen plan, gratis tester inkludert. Prøv den gratis OG-bildetesteren for å se rørledningen i aksjon, og signer deretter dine egne URL-er med stegene nedenfor.

Slik fungerer det — Arkitektur

FastOG er bygget for on-demand OG-bilder: nettstedet ditt signerer en URL ved side-rendering, crawleren henter den, FastOG renderer PNG-en én gang, og Cloudflare cacher den slik at alle senere hentinger er øyeblikkelige og 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 reglene som gjør at det «bare fungerer»

  1. Signer innholdet, ikke klokken. Signaturen dekker alle parametere (tittel, mal, …). Identiske parametere produserer alltid identisk URL — det er det som får edge-cachen til å fungere: samme innhold → samme URL → cache-treff for alltid.
  2. Bildet er permanent etter første rendering. Signaturen styrer bare den første renderingen. Når et bilde først er rendret, beholder FastOGs edge det i ett år, nøklet på innholdsparameterne — senere forespørsler med samme parametere serveres fra edge og når aldri rendereren (teller ikke mot kvoten din). En ugyldig signatur på hurtigbufret innhold returnerer fortsatt det bufrede PNG-bildet (svaret rapporterer hvilket nivå som svarte: X-FastOG-Worker-Cache: HIT eller R2); bare et cache-miss får 401.
  3. Den eneste feilmodusen. En manipulert URL (noen endrer title/template) får 401 hos FastOG ved cache-miss — så den kan aldri brukes til å bruke opp kvoten din. Legitime URL-er utløper aldri: de forblir gyldige til du roterer nøkkelens HMAC-hemmelighet.

1. Få tak i API-nøkkelen og HMAC-hemmeligheten din

  1. Gå til FastOG-dashbordet → API-nøkler

  2. Klikk på "Opprett ny API-nøkkel"

  3. Gi den et navn (f.eks. "Produksjonsnettsted")

  4. Kopier begge verdiene (vises bare én gang!):

    • API-nøkkel: sk_... (32 tegn etter prefikset)
    • HMAC-hemmelighet: 64-tegns heksadesimal streng
  5. Lagre dem i miljøet ditt:

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. Implementer URL-signeringsfunksjonen

For komplette veiledninger med flere rammeverk og mønstre, 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. Bruk i bloggen eller nettstedet ditt

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. Test implementeringen din

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

Forventet:

  • ✅ Gyldig signatur → Returnerer OG-bilde
  • ❌ Endret parameter (f.eks. title endret) → {"code": "invalid_signature"}
  • ❌ Ugyldig signatur → {"code": "invalid_signature"}

5. Sikkerhetssjekkliste

  • [ ] HMAC-hemmelighet lagret i miljøvariabler (ikke i koden)
  • [ ] Bruker HTTPS for alle forespørsler
  • [ ] Hemmeligheten er aldri eksponert i JavaScript på klientsiden
  • [ ] Overvåking av mislykkede signaturforsøk

6. Håndter feil

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

Neste steg

  1. Implementer signering i backend-en til nettstedet ditt (se veiledningene)
  2. Test med FastOG-API-et
  3. Overvåk logger for mislykkede signeringsforsøk
  4. Sett opp varsling for gjentatte feil

For full dokumentasjon, se HMAC-signaturveiledningen.

Full API-referanse

Prøv gratis – ingen registrering nødvendig

Forhåndsvis og test dynamiske OG-bilder på sekunder.

Åpne den gratis OG-bildetesteren

A BeCaSimulations site