Hugo is one of the fastest static site generators out there. But unlike WordPress or Next.js, it doesn't generate OG images for you out of the box. You have to wire them up yourself.
I run my own site on Hugo, so I've been through this. Here's exactly how to set up OG images — from a simple default image to per-page customization.
The Quick Route: One Default OG Image
If every page on your site can share the same OG image, this takes two minutes. Add these tags to your theme's layouts/partials/head.html (or wherever your <head> lives):
<meta property="og:image" content="https://yoursite.com/og-default.png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:type" content="image/png">
That's it. Every page on your site will now show this image when shared. Use our free OG image generator to create a clean 1200×630 default — pick a template, add your site name, download, and reference it.
This works, but every share looks identical regardless of what the page is about. If you publish regularly, you'll want per-page images.
Per-Page OG Images via Front Matter
Hugo makes this easy. Add an image field to your content's front matter:
---
title: "My Blog Post"
date: "2026-07-03"
image: "/og/my-post.png"
---
Then in your head.html partial, switch from a hardcoded URL to Hugo's template logic:
{{- $ogImage := "https://yoursite.com/og-default.png" -}}
{{- if .Params.image -}}
{{- $ogImage = .Params.image | absURL -}}
{{- end -}}
<meta property="og:image" content="{{ $ogImage }}">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
Now each page can have its own OG image. Pages without an image field fall back to the default. The absURL function resolves relative paths like /og/my-post.png into full URLs — critical because OG image URLs must be absolute.
Adding og:title and og:description
While you're in there, add the other essential OG tags. Hugo has Page.Title and Page.Description (or Page.Summary as fallback):
<meta property="og:title" content="{{ .Title }}">
<meta property="og:description" content="{{ .Description | default .Summary }}">
<meta property="og:url" content="{{ .Permalink }}">
<meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}">
<meta property="og:site_name" content="Your Site Name">
This gives every page a complete set of Open Graph tags with zero manual work per post — just set the title and description in front matter as you normally would.
Generating the Actual Images
Now you need actual 1200×630 PNG files. You have a few options:
- Manual (Canva/Figma): Create a canvas, design each image by hand. Works for occasional posts, painful if you publish weekly.
- Our generator: Open ogimgen.com, pick a template, type the title, download. Takes 30 seconds per image. No design skills needed.
- Automated script: Write a Hugo pipeline that generates images at build time (possible but requires tooling setup).
For most Hugo bloggers, the generator is the sweet spot. Create a batch of images for your upcoming posts in one sitting, drop them in static/og/, reference them in front matter, and you're done.
Testing Your Setup
After deploying, verify your OG tags are correct. Our free OG Checker shows a preview of your image and all detected meta tags. Paste your page URL and check:
- Is the correct og:image showing?
- Are og:title and og:description present?
- Is og:image:width/height included?
- Is the og:type correct (article vs website)?
Then test on actual platforms — share the URL on Facebook, Twitter, or Discord and confirm the preview looks right. If Facebook shows a stale image, run it through the Sharing Debugger to force a fresh scrape.
Common Hugo-Specific Issues
- Relative vs absolute URLs: OG image URLs must be absolute (starting with
https://). Hugo'sabsURLhandles this, but only if your site is configured withbaseURLinhugo.yaml. Double-check your config. - OG images inside
static/vsassets/: Images instatic/are copied as-is. Images inassets/go through Hugo's asset pipeline (resources.Get) — more flexible but requiresPermalinkinstead ofabsURL. For OG images,static/is simpler. - Minification stripping attribute quotes: If you use Hugo's
--minifyflag, check that your OG meta tags still have proper syntax. Minifiers can striparia-labelandmetaattribute quotes in some configurations. Add| safeHTMLin your template if needed. - Schema markup: OG tags alone aren't enough for appearing in AI search results. Consider adding
ArticleorBreadcrumbListJSON-LD schemas — AI crawlers like GPTBot and ClaudeBot prioritize pages with structured data.
Full Template Example
Here's a complete layouts/partials/head-og.html you can drop into your Hugo theme:
{{- $ogImage := "https://yoursite.com/og-default.png" -}}
{{- if .Params.image -}}
{{- $ogImage = .Params.image | absURL -}}
{{- end -}}
<meta property="og:title" content="{{ .Title }}">
<meta property="og:description" content="{{ .Description | default .Summary }}">
<meta property="og:url" content="{{ .Permalink }}">
<meta property="og:image" content="{{ $ogImage }}">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}">
<meta property="og:site_name" content="Your Site Name">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{{ .Title }}">
<meta name="twitter:description" content="{{ .Description | default .Summary }}">
<meta name="twitter:image" content="{{ $ogImage }}">
Include it from your main head.html with {{- partial "head-og.html" . -}}. The . (dot) passes the page context so Hugo can access .Title, .Params.image, etc.
Ready to create your first OG image? Use our generator — it's free, no signup, and takes 30 seconds per image.