ogmake

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.