Tema

Inicio rápido: genera tu primera imagen OG

Este inicio rápido de FastOG pone en marcha tu primera imagen dinámica de Open Graph en cuestión de minutos. Registrarse es gratis; después elige un plan de Dynamic API para la cuota de renderizado. Creas tu clave de API y tu secreto HMAC bajo demanda en el panel de control. Cada renderizado es limpio, sin marca de agua en ningún plan, incluido el probador gratuito. Prueba el Probador de imágenes OG gratuito para ver el pipeline en acción y luego firma tus propias URLs con los pasos que se indican a continuación.

Cómo funciona: arquitectura

FastOG está diseñado para imágenes OG bajo demanda: tu sitio web firma una URL en el momento de renderizar la página, el rastreador la obtiene, FastOG renderiza el PNG una vez y Cloudflare lo almacena en caché para que cada solicitud posterior sea instantánea y gratuita.

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

Las tres reglas que hacen que "simplemente funcione"

  1. Firma el contenido, no el reloj. La firma cubre todos los parámetros (título, plantilla, …). Parámetros idénticos siempre producen la misma URL — eso es lo que hace funcionar la caché de borde: mismo contenido → misma URL → acierto de caché para siempre.
  2. La imagen es permanente después del primer renderizado. La firma solo protege el primer renderizado. Una vez que se renderiza una imagen, el borde de FastOG la conserva durante un año con clave basada en los parámetros de contenido — las solicitudes posteriores con los mismos parámetros se sirven desde el borde y nunca llegan al renderizador (no cuentan contra tu cuota). Una firma inválida en contenido cacheado sigue devolviendo el PNG cacheado (la respuesta indica qué nivel respondió: X-FastOG-Worker-Cache: HIT o R2); solo un fallo de caché recibe un 401.
  3. El único modo de fallo. Una URL manipulada (alguien cambia title/template) recibe 401 en FastOG ante un fallo de caché — por lo que nunca puede usarse para gastar tu cuota. Las URLs legítimas nunca caducan: siguen siendo válidas hasta que rote el secreto HMAC de la clave.

1. Obtén tu clave de API y secreto HMAC

  1. Ve a Panel de FastOG → Claves de API

  2. Haz clic en "Crear nueva clave de API"

  3. Ponle un nombre (por ejemplo, "Sitio web de producción")

  4. Copia ambos valores (¡solo se muestran una vez!):

    • Clave de API: sk_... (32 caracteres después del prefijo)
    • Secreto HMAC: cadena hexadecimal de 64 caracteres
  5. Guárdalos en tu entorno:

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. Implementar la función de firma de URL

Para tutoriales completos con múltiples frameworks y patrones, consulta:

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. Uso en tu blog o sitio web

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>

Plantilla de Django

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. Prueba tu implementación

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

Resultado esperado:

  • ✅ Firma válida → Devuelve la imagen OG
  • ❌ Parámetro manipulado (p. ej. title cambiado) → {"code": "invalid_signature"}
  • ❌ Firma no válida → {"code": "invalid_signature"}

5. Lista de verificación de seguridad

  • [ ] Secreto HMAC almacenado en variables de entorno (no en el código)
  • [ ] Uso de HTTPS para todas las solicitudes
  • [ ] El secreto nunca se expone en JavaScript del lado del cliente
  • [ ] Supervisión de intentos de firma fallidos

6. Gestionar errores

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

Siguientes pasos

  1. Implementa la firma en el backend de tu sitio web (consulta los tutoriales)
  2. Prueba con la API de FastOG
  3. Supervisa los registros para detectar intentos de firma fallidos
  4. Configura alertas para fallos repetidos

Para la documentación completa, consulta la Guía de firmas HMAC.

Referencia completa de la API

Pruébalo gratis: no se requiere registro

Previsualiza y prueba imágenes OG dinámicas en segundos.

Abrir el probador gratuito de imágenes OG

A BeCaSimulations site