Démarrage rapide — Générez votre première image OG
Ce démarrage rapide FastOG met en ligne votre première image Open Graph dynamique en quelques minutes. L’inscription est gratuite — choisissez ensuite un forfait Dynamic API pour le quota de rendu. Vous créez votre clé API et votre secret HMAC à la demande dans le tableau de bord. Chaque rendu est propre — aucun filigrane, quel que soit le forfait, testeur gratuit inclus. Essayez le testeur d’images OG gratuit pour voir le pipeline en action, puis signez vos propres URL avec les étapes ci-dessous.
Fonctionnement — Architecture
FastOG est conçu pour les images OG à la demande : votre site signe une URL au moment du rendu de la page, le crawler la récupère, FastOG génère le PNG une seule fois, et Cloudflare le met en cache afin que chaque requête ultérieure soit instantanée et gratuite.
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
Les trois règles qui font que « ça fonctionne tout simplement »
- Signez le contenu, pas l'horloge. La signature couvre chaque paramètre (titre, template, …). Des paramètres identiques produisent toujours la même URL — c'est ce qui fait fonctionner le cache périphérique : même contenu → même URL → hit de cache à vie.
- L'image est permanente après le premier rendu. La signature ne contrôle que le premier rendu. Une fois l'image générée, la périphérie de FastOG la conserve pendant un an, indexée sur les paramètres de contenu — les requêtes ultérieures avec les mêmes paramètres sont servies depuis la périphérie et n'atteignent jamais le moteur de rendu (elles ne comptent pas dans votre quota). Une signature invalide sur un contenu en cache renvoie quand même le PNG mis en cache (la réponse indique quel niveau a répondu :
X-FastOG-Worker-Cache: HITouR2) ; seul un miss de cache renvoie une erreur 401. - Le seul mode d'échec. Une URL falsifiée (quelqu'un modifie
title/template) reçoit une 401 de FastOG en cas de miss de cache — elle ne peut donc jamais être utilisée pour consommer votre quota. Les URL légitimes n'expirent jamais : elles restent valides jusqu'à ce que vous fassiez pivoter le secret HMAC de la clé.
1. Obtenez votre clé API et votre secret HMAC
-
Accédez à Tableau de bord FastOG → Clés API
-
Cliquez sur « Créer une nouvelle clé API »
-
Donnez-lui un nom (par exemple, « Site de production »)
-
Copiez les deux valeurs (affichées une seule fois !) :
- Clé API :
sk_...(32 caractères après le préfixe) - Secret HMAC : chaîne hexadécimale de 64 caractères
- Clé API :
-
Stockez-les dans votre environnement :
# 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. Implémenter la fonction de signature d’URL
Pour des tutoriels complets couvrant plusieurs frameworks et schémas, consultez :
- Next.js App Router
- Next.js Pages Router
- React
- SvelteKit
- Vue / Nuxt
- Astro
- Express
- Vanilla Node.js
- JavaScript
- PHP / Laravel
- Python
- Ruby / Rails
Node.js / Next.js
// 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)
// 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)
# 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. Utilisation dans votre blog ou site web
Next.js App Router
// 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
{{-- 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>
Modèle 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. Testez votre implémentation
# 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>"
Résultat attendu :
- ✅ Signature valide → Renvoie l’image OG
- ❌ Paramètre modifié (par ex.
titlechangé) →{"code": "invalid_signature"} - ❌ Signature invalide →
{"code": "invalid_signature"}
5. Liste de contrôle de sécurité
- [ ] Secret HMAC stocké dans des variables d’environnement (pas dans le code)
- [ ] Utilisation de HTTPS pour toutes les requêtes
- [ ] Secret jamais exposé dans le JavaScript côté client
- [ ] Surveillance des tentatives de signature échouées
6. Gérer les erreurs
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;
}
}
Prochaines étapes
- Implémentez la signature dans le backend de votre site web (voir les tutoriels)
- Testez avec l’API FastOG
- Surveillez les journaux pour détecter les tentatives de signature échouées
- Configurez des alertes en cas d’échecs répétés
Pour la documentation complète, consultez le Guide de signature HMAC.