OG images in Hugo
Hugo builds a static site — there's no server or build step that can compute an HMAC
signature, so the signed GET /i/{keyId}/{sig}
route (Signing) isn't reachable from a template. Use a
public key instead: it accepts unsigned requests for the
templates you explicitly allow, which is all a Hugo partial needs.
1. Create a public key
Sign in at /login, open the dashboard, check which templates the key
may render (e.g. blog), then "Create public key". The key id
(starts with k_) is safe to commit into your site's source —
it's not the bearer secret.
Still on a hand-issued beta key? Email support@ogmake.com.
2. Add it to the head partial
In layouts/partials/opengraph.html (included from
baseof.html's <head>):
<meta property="og:image" content="https://ogmake.com/p/k_yourkeyid/blog?site=example.com/blog&author={{ .Params.author | urlquery }}&date={{ .Date.Format "Jan 2, 2006" | urlquery }}&title={{ .Title | plainify | urlquery }}" /> Include it once from your base template so every page — post, section, taxonomy — gets a tag built from its own front matter:
<!-- layouts/_default/baseof.html -->
<head>
...
{{ partial "opengraph.html" . }}
</head> Gotcha: an un-urlquery'd title breaks the URL, not just the image
.Title and .Params.* often contain
spaces, &, or quotes straight from front matter — pass every
value through Hugo's urlquery function before it lands in the
query string, or an ampersand in a title silently truncates the params after it (the rest of
the string gets parsed as a new, wrong query key) rather than producing a visible error.
.Title can also carry Markdown/HTML from a shortcode-heavy title
— pipe it through plainify first so the API template (which
renders plain text, not markup) doesn't get raw tags in its title field.
Verify it
hugo server runs on localhost, which
social crawlers can't reach — check the tag renders correctly locally (curl -s http://localhost:1313/posts/some-post/ | grep 'og:image'), but do the real preview check against the deployed URL. Because the image URL is built
from front matter, an unsigned request with a bad or missing field still returns a
200 fallback image rather than an error page — inspect the
response header instead: curl -I "https://ogmake.com/p/..."
and look for x-ogmake-error (see
debugging a public URL). Once the tag is right, run
the live page through Facebook's
Sharing Debugger, or compose a new post on X with the URL and check the card renders
there (X retired its public Card Validator in 2022) — since a Hugo rebuild that only changes
template design (not front matter)
produces the exact same image URL, add a v query param tied to
your build (e.g. Hugo's hugo.Version or a deploy timestamp) so a
pure design change still gets a fresh crawl instead of a stale cached preview.