Chủ đề

Bắt đầu nhanh — Tạo Ảnh OG Đầu Tiên Của Bạn

Hướng dẫn bắt đầu nhanh FastOG này giúp bạn đưa ảnh Open Graph động đầu tiên lên trực tuyến chỉ trong vài phút. Đăng ký hoàn toàn miễn phí — sau đó chọn gói Dynamic API để có hạn mức render. Bạn tạo khóa API và bí mật HMAC theo nhu cầu trong bảng điều khiển. Mỗi lần render đều sạch — không có hình mờ trên bất kỳ gói nào, bao gồm cả gói thử miễn phí. Hãy thử Trình Kiểm Tra Ảnh OG Miễn Phí để xem quy trình hoạt động, sau đó ký URL của riêng bạn theo các bước dưới đây.

Cách hoạt động — Kiến trúc

FastOG được xây dựng cho hình ảnh OG theo yêu cầu: website của bạn ký một URL tại thời điểm render trang, trình thu thập dữ liệu (crawler) tải URL đó, FastOG render PNG một lần, và Cloudflare lưu vào bộ nhớ đệm (cache) để mọi lần tải sau đều tức thì và miễn phí.

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

Ba quy tắc giúp nó "hoạt động ngay"

  1. Ký nội dung, không ký thời gian. Chữ ký bao phủ mọi tham số (tiêu đề, template, …). Các tham số giống hệt nhau luôn tạo ra URL giống hệt nhau — đó là điều khiến bộ nhớ đệm biên (edge cache) hoạt động: cùng nội dung → cùng URL → luôn trúng cache.
  2. Hình ảnh là vĩnh viễn sau lần render đầu tiên. Chữ ký chỉ kiểm soát lần render đầu tiên. Sau khi hình ảnh được render, biên FastOG giữ nó trong một năm theo khóa là các tham số nội dung — các yêu cầu sau với cùng tham số được phục vụ từ biên và không bao giờ chạm tới trình render (không tính vào hạn mức của bạn). Chữ ký sai trên nội dung đã được cache vẫn trả về PNG đã cache (phản hồi báo cáo tầng nào đã trả lời: X-FastOG-Worker-Cache: HIT hoặc R2); chỉ khi cache trượt (miss) mới bị trả về 401.
  3. Chế độ lỗi duy nhất. Một URL bị can thiệp (ai đó thay đổi title/template) sẽ nhận 401 tại FastOG khi cache trượt — vì vậy nó không bao giờ có thể được dùng để tiêu tốn hạn mức của bạn. URL hợp lệ không bao giờ hết hạn: chúng vẫn hợp lệ cho đến khi bạn xoay vòng bí mật HMAC của khóa.

1. Lấy API Key và HMAC Secret của bạn

  1. Truy cập FastOG Dashboard → API Keys

  2. Nhấp vào "Create New API Key"

  3. Đặt tên cho nó (ví dụ: "Production Website")

  4. Sao chép cả hai giá trị (chỉ hiển thị một lần!):

    • API Key: sk_... (32 ký tự sau tiền tố)
    • HMAC Secret: chuỗi hex gồm 64 ký tự
  5. Lưu chúng vào biến môi trường của bạn:

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. Triển khai Hàm Ký URL

Để xem các hướng dẫn hoàn chỉnh với nhiều framework và mẫu thiết kế, hãy tham khảo:

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. Sử dụng trong Blog/Website của bạn

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. Kiểm Tra Quá Trình Triển Khai Của Bạ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>"

Kết quả mong đợi:

  • ✅ Chữ ký hợp lệ → Trả về hình ảnh OG
  • ❌ Tham số bị thay đổi (ví dụ: title bị sửa) → {"code": "invalid_signature"}
  • ❌ Chữ ký không hợp lệ → {"code": "invalid_signature"}

5. Danh sách kiểm tra bảo mật

  • [ ] Khóa bí mật HMAC được lưu trong biến môi trường (không nằm trong mã nguồn)
  • [ ] Sử dụng HTTPS cho tất cả các yêu cầu
  • [ ] Khóa bí mật không bao giờ bị lộ trong JavaScript phía máy khách
  • [ ] Giám sát các lần thử chữ ký thất bại

6. Xử Lý Lỗi

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

Các bước tiếp theo

  1. Triển khai ký (signing) trong backend của website bạn (xem các hướng dẫn)
  2. Kiểm thử với FastOG API
  3. Theo dõi nhật ký (logs) để phát hiện các lần ký thất bại
  4. Thiết lập cảnh báo cho các lần thất bại lặp lại

Để xem tài liệu đầy đủ, hãy tham khảo Hướng dẫn chữ ký HMAC.

Tài liệu tham khảo API đầy đủ

Dùng thử miễn phí — không cần đăng ký

Xem trước và kiểm tra hình ảnh OG động trong vài giây.

Mở Trình kiểm tra hình ảnh OG miễn phí

A BeCaSimulations site