Guides

Signed URLs and domain allowlists

When an OGKit image URL is public (embedded in HTML that anyone can view), a raw API key in the query string is a credential leak. Signed URLs and domain allowlists let you keep keys server-side while still serving deterministic 1200×630 PNGs to Slack, LinkedIn, Discord, and crawlers.

When you need signing

Use require-signed-urls on a key when the image URL will appear in public HTML, sitemaps, or RSS. Scrapers fetch the URL without cookies; they do not need your key — they need a URL that already includes a valid HMAC signature (or a key that does not require signing).

Domain allowlists are complementary: if a key has allowed domains, pass domain=example.com (or your claim) and OGKit rejects requests whose domain claim is outside the list.

  • Public marketing pages and blog posts → prefer signed URLs
  • Internal previews / CI → demo=1 or a key without signing is fine
  • Multiple brands on one account → one key per domain allowlist

How signing works

Enable “Require signed URLs” on the key in the dashboard. Then every GET to /api/og/{template} (and /api/og/auto) with that key must include sig=…

Canonical string: pathname + ? + sorted query string with sig removed. Sign with HMAC-SHA256 using the full API key (ogk_live_…) as the secret. Digest is lowercase hex.

Important: sort query parameters before hashing. Changing parameter order without re-sorting produces a different signature and returns invalid_signature.

import { createHmac } from "node:crypto";

function signOgUrl(rawUrl: string, apiKey: string) {
  const url = new URL(rawUrl);
  const params = new URLSearchParams(url.searchParams);
  params.delete("sig");
  params.sort();
  const query = params.toString();
  const canonical = query ? `${url.pathname}?${query}` : url.pathname;
  const sig = createHmac("sha256", apiKey).update(canonical).digest("hex");
  params.set("sig", sig);
  url.search = params.toString();
  return url.toString();
}

const unsigned = new URL("https://www.webmorp.art/ogkit/api/og/article");
unsigned.searchParams.set("key", process.env.OGKIT_KEY!);
unsigned.searchParams.set("title", "Ship notes");
unsigned.searchParams.set("domain", "example.com");
const imageUrl = signOgUrl(unsigned.toString(), process.env.OGKIT_KEY!);
// → put imageUrl in metadata.openGraph.images

Python example

import hashlib, hmac
from urllib.parse import urlencode, urlparse, parse_qsl, urlunparse

def sign_og_url(raw_url: str, api_key: str) -> str:
    parts = urlparse(raw_url)
    params = [(k, v) for k, v in parse_qsl(parts.query, keep_blank_values=True) if k != "sig"]
    params.sort(key=lambda kv: kv[0])
    query = urlencode(params)
    canonical = f"{parts.path}?{query}" if query else parts.path
    sig = hmac.new(api_key.encode(), canonical.encode(), hashlib.sha256).hexdigest()
    params.append(("sig", sig))
    return urlunparse(parts._replace(query=urlencode(params)))

Errors

  • missing_signature — key requires signing but sig is absent
  • invalid_signature — HMAC mismatch (wrong key, unsorted params, or mutated query)
  • domain_not_allowed — domain claim not in the key allowlist

Operational tips

Sign on the server at request time (or at build time for static pages). Never ship the raw API key to the browser just to compute sig.

If you change title/subtitle after deploy, regenerate the signature — the signed URL is bound to the exact query string.

During open access, signing is optional for evaluation, but still recommended once URLs are public.

FAQ

Do social networks need my API key?

No. Scrapers only fetch the final HTTPS image URL. Signing lets that URL be public without embedding a usable bare key.

Can I sign demo=1 URLs?

demo=1 skips API key auth, so signatures are not required. Use signing with a real key for production metadata.

What is the domain parameter for?

Optional claim checked against the key’s allowlist. Use it when one account serves multiple properties and each key is scoped to a hostname.

Related

OGKit turns one HTTPS URL into a 1200×630 Open Graph image. Read the API reference, deep guides, the Open Graph SEO guide, try the Playground, or sign in to create API keys.