Auto-generate OG images in Hugo

Hugo can sign ogsmith URLs natively — its crypto.HMAC template function plus querify produce exactly the canonical string the API expects. Pure templates, no build scripts, works on any static host.

Example Open Graph image for a Hugo site rendered by ogsmith

1. Get a key

Grab a free key from the homepage, then put the credentials in your site config (or better, environment-injected params on your CI):

# hugo.toml
[params.ogsmith]
kid = "ab12cd34"
secret = "…"   # keep out of public repos — use HUGO_PARAMS_OGSMITH_SECRET in CI

2. Add a partial

Create layouts/partials/ogsmith.html. Hugo's querify sorts keys and percent-encodes values (Go turns spaces into +, so one replace makes it RFC 3986 — the API's canonical form):

{{- $p := dict
    "kid" site.Params.ogsmith.kid
    "title" .Title
    "description" (.Description | default .Summary | plainify | truncate 140)
    "site" site.Title
    "template" "article"
-}}
{{- $canonical := replace (querify $p) "+" "%20" -}}
{{- $sig := crypto.HMAC "sha256" site.Params.ogsmith.secret $canonical -}}
<meta property="og:image" content="https://api.ogsmith.dev/v1/og?{{ $canonical | safeHTMLAttr }}&sig={{ $sig }}">
<meta name="twitter:card" content="summary_large_image">

3. Include it in your head

{{/* layouts/_default/baseof.html, inside <head> */}}
{{ partial "ogsmith.html" . }}

Every page — posts, sections, the homepage — now emits a signed, cached social card URL at build time.

Frequently asked questions

Why does the replace(querify …) trick work?

Go's querify sorts parameters by key and percent-encodes everything except RFC 3986 unreserved characters, using + for spaces. The ogsmith canonical form is the same encoding with %20 for spaces — so a single replace produces a byte-identical canonical string, and crypto.HMAC over it matches the server's signature.

Is it safe to put the signing secret in hugo.toml?

Only if the repo is private. For public repos, set HUGO_PARAMS_OGSMITH_SECRET as a CI environment variable (Netlify/Cloudflare Pages/GitHub Actions all support this) — Hugo maps it to site.Params.ogsmith.secret automatically.

Why signed URLs instead of an API key?

The og:image URL ends up in your public HTML, where anyone can read it. A signed URL exposes no secret: the HMAC signature covers every parameter, so changing the title (or anything else) invalidates it. A leaked signed URL can only ever render that exact image.

Does every page view burn my quota?

No. Images are served through CloudFront with immutable caching, and cache hits don't count against your quota. A given og:image URL renders once; every crawler and preview after that is a free edge hit.

Templates available: basic, gradient, split, article, minimal, code — try them in the playground. Full parameter list in the docs.

Add OG images to your Hugo site now

Free key, 250 images a month, no card. One GET request and every page has a social card.

Get a Free API KeyTry the Playground