ScreenshotNeo

BlogHow-to

How to Add Open Graph Metadata to a Hugo Website

Add Open Graph metadata to Hugo with its embedded partial, page front matter, and site defaults. Build the site and verify the rendered tags and image URL.

By the ScreenshotNeo team4 October 20269 min read

To add Open Graph metadata to a Hugo website, include Hugo’s embedded opengraph.html partial in the document head, set page-specific values in front matter, and configure site-wide defaults. Then build the site and inspect the generated HTML to confirm the title, type, image, and canonical URL.

Open Graph metadata is a set of properties in the page’s HTML <head>. The protocol’s four required properties are og:title, og:type, og:image, and og:url. The [Open Graph Protocol](https://ogp.me/) also defines optional properties such as og:description, og:locale, and og:site_name.

1. Check the existing Hugo head template

Before changing templates, find where your theme or site renders the document head. Check whether it already calls the Open Graph partial or emits these properties. Adding a second implementation can produce duplicate tags, leaving crawlers with ambiguous values.

Hugo provides an embedded Open Graph partial. Add this call to the head template if it is not already there:

{{ partial "opengraph.html" . }}

For example, a minimal head partial might look like this:

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>{{ .Title }}</title>
  {{ partial "opengraph.html" . }}
</head>

Place the call inside the rendered <head>. Keep the rest of your theme’s head content as needed. Hugo documents that the embedded partial can be overridden by copying its source to layouts/_partials/opengraph.html. Only override it when you have a specific output requirement that the built-in behavior does not satisfy; first inspect the rendered output to see what it already provides. See [Hugo’s Open Graph template documentation](https://gohugo.io/templates/embedded/#open-graph).

2. Set page-level metadata in front matter

Give each page its own title, description, and representative image where appropriate. For a Markdown content file, YAML front matter can look like this:

---
title: "A practical guide to Hugo templates"
description: "Learn how Hugo templates combine content and layout to generate a static site."
images:
  - "images/hugo-templates-cover.png"
date: 2026-10-04
lastmod: 2026-10-04
---

Use the image path that corresponds to a real page resource or global resource in your site. Hugo’s embedded partial can process up to six values in the images front matter parameter. For an internal path, it searches page resources and then global resources. If it finds a resource, it uses that resource’s permalink; otherwise it converts the path to an absolute URL. An external image URL is used as supplied.

Hugo’s documented fallback behavior means you do not need to repeat every value on every page:

Property Fallback behavior Practical guidance
og:title Page title, then site title, then params.title. Set a descriptive page title; confirm the site title is configured as a sensible fallback.
og:site_name Site title, then params.title. Use a consistent site name in configuration.
og:description Page description, then page summary, then params.description. Use a page description when the social summary should differ from Hugo’s content summary.
og:locale Page locale front matter, then the site language’s locale. Hugo emits hyphens as underscores, for example en-US becomes en_US.

description and summary are different Hugo fields. The description is commonly used for a head meta element; the summary is a content summary or teaser and can act as the documented Open Graph description fallback. Set the field that matches the meaning of your content rather than relying on an unintended fallback. See [Hugo page variables](https://gohugo.io/content-management/front-matter/) and the [Open Graph partial behavior](https://gohugo.io/templates/embedded/#open-graph).

3. Configure site-wide defaults

Set defaults in your existing Hugo configuration file. Do not add a second configuration format to a site that already uses one. For example, with YAML configuration:

baseURL: "https://www.example.com/"
title: "Example Site"
languageCode: "en-us"
params:
  title: "Example Site"
  description: "Guides and notes about building with Hugo."
  images:
    - "images/default-social-card.png"

Hugo supports configuration in YAML, TOML, and JSON. The example above uses YAML; retain your project’s current format and its existing settings. The baseURL matters because Hugo’s embedded partial emits the page permalink for og:url, and image paths may be turned into absolute URLs. The site-wide params.images value is a fallback image when a page has no suitable image.

For a site using TOML, the equivalent settings are commonly represented with TOML syntax:

baseURL = "https://www.example.com/"
title = "Example Site"
languageCode = "en-us"

[params]
  title = "Example Site"
  description = "Guides and notes about building with Hugo."
  images = ["images/default-social-card.png"]

Consult [Hugo’s configuration documentation](https://gohugo.io/configuration/) for the exact structure supported by your project and Hugo version. Hugo templates can access custom parameters through .Site.Params.

4. Choose a reliable Open Graph image

A page’s Open Graph image should represent that page and resolve to a publicly reachable image URL when the site is deployed. Hugo’s embedded partial looks for images in this order:

  1. Values in the page’s images front matter parameter.
  2. A page resource whose filename matches *feature*.
  3. A page resource matching *cover*.
  4. A page resource matching *thumbnail*.
  5. The first entry in site configuration’s params.images array, if set.

Hugo may emit up to six og:image tags from the configured values. If a page needs a particular image, specify it explicitly in that page’s front matter. When checking the build, make sure the resulting URL uses the intended site host and path, and that the deployed image exists at that URL.

5. Understand page type and article properties

Hugo’s embedded partial sets og:type to article for regular pages and website for list and home pages. On article pages it also emits article:section, article:published_time, article:modified_time, and up to the first six article:tag values. Check these generated values before adding manual copies in a custom template.

The Open Graph Protocol defines og:url as the canonical URL and permanent identifier for the object. Hugo emits the page permalink, so your base URL and permalink settings need to reflect the canonical address you intend to publish. If the generated URL has the wrong host, path, or trailing-slash convention, correct the site configuration or permalink setup rather than hard-coding a different value into duplicate tags.

6. Build and verify the generated HTML

  1. Build the site using the same Hugo command and configuration you use for deployment.
  2. Open the generated HTML file for a regular article and inspect its <head>.
  3. Confirm that the expected properties appear once and contain the intended title, page type, description, canonical URL, locale, and image URL.
  4. Repeat the check on the home page or a list page, since Hugo emits different og:type values for those page kinds.
  5. Check that each image URL resolves on the deployed site. A local file existing in the repository does not by itself prove that the published URL is correct.

For a quick source inspection from a shell, point rg at the generated HTML file or output directory:

rg -n 'og:(title|type|image|url|description|locale|site_name)' public/

Replace public/ if your configured publish directory differs. This inspects generated source; it does not fetch or validate images on the deployed host.

Common problems and fixes

Symptom Likely cause Fix
No Open Graph tags appear. The head template never calls the embedded partial, or a different head template is being used. Trace the layout used by that page, add {{ partial "opengraph.html" . }} inside its head, then build and inspect the output.
Each property appears more than once. The theme already emits tags and the site added another partial or custom tags. Keep one source for each property. Inspect the theme’s head partial and remove the duplicate implementation.
The description is missing or unexpected. The page has no description or summary, and the site has no params.description; or a summary fallback is not the intended social copy. Set a page-level description for that page and a useful site default for pages without one.
The wrong image is selected. A page resource matches the feature, cover, or thumbnail fallback, or only a site default is configured. Set the page’s images value explicitly and verify the emitted URL in the built HTML.
The image URL has the wrong host or is not absolute. The resource path or baseURL does not match the production site setup. Check the actual resource permalink and production base URL. Confirm the built tag points at the intended deployed address.
og:url points to an unexpected address. The page permalink, base URL, or permalink configuration differs from the canonical URL you expect. Correct those settings and rebuild; Hugo’s embedded partial uses the page permalink.
Article properties are absent or wrong. The page is not rendered as a regular article, or date, section, or tag metadata is missing or different from the intended content. Check the page kind and its front matter, then inspect Hugo’s emitted article properties before customizing the partial.
Front matter changes do not show up. You inspected a stale build, edited a different content file, or used a field outside the documented fallback chain. Rebuild the correct site/configuration, inspect the matching generated page, and use recognized fields such as title, description, summary, images, and locale.

Performance, reliability, and cost

Open Graph tags are generated as part of the page’s HTML head, so this implementation does not require a screenshot service or a client-side script to create the metadata. Hugo’s documented partial and front matter fallbacks let you keep defaults in configuration and override them for individual pages. The practical reliability checks are to avoid duplicate tags, ensure the canonical URL is right, and verify that the image is present at its deployed URL.

Building this metadata with Hugo does not add a separate service charge. The time cost comes from creating and maintaining representative images and checking the generated output when templates, paths, or site configuration change. No engagement or traffic uplift should be assumed from adding tags alone.

Or skip the browser setup

For a rendered screenshot of your Hugo page, one GET request to [ScreenshotNeo](https://screenshotneo.com) returns an image or PDF. ScreenshotNeo is a website screenshot API and MCP server from Yorker Media; it is for capturing the rendered page, while Hugo’s Open Graph tags remain page metadata. See the [ScreenshotNeo API docs](https://screenshotneo.com/docs/) for request options.

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://www.example.com/guides/hugo-templates/ \
  -o shot.webp

Equivalent Python request:

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={
        "access_key": "YOUR_API_KEY",
        "url": "https://www.example.com/guides/hugo-templates/",
    },
    timeout=90,
)
open("shot.webp", "wb").write(r.content)

Equivalent Node.js request:

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://www.example.com/guides/hugo-templates/',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`ScreenshotNeo returned ${res.status}`);
await Bun.write('shot.webp', new Uint8Array(await res.arrayBuffer()));

With Node.js versions that do not provide Bun’s file writer, save the response with Node’s built-in filesystem API:

import { writeFile } from 'node:fs/promises';

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://www.example.com/guides/hugo-templates/',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`ScreenshotNeo returned ${res.status}`);
await writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits are never billed; response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for AI agents. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. [Create a free ScreenshotNeo account](https://screenshotneo.com/account/sign-up/).

FAQ

Does Hugo have a built-in Open Graph template?

Yes. Hugo includes an embedded partial named opengraph.html. Call it from the page head and supply metadata through page front matter and site configuration.

Do I need to add og:type manually?

Usually not when using Hugo’s embedded partial. It emits article for regular pages and website for list and home pages.

Can the Open Graph image be an external URL?

Yes. Hugo’s embedded partial uses external image URLs as supplied. Verify that the selected URL is the one you intend to publish.

Does an Open Graph image replace the page’s regular HTML metadata?

No. Open Graph properties are a separate set of head metadata. Keep the page’s ordinary title and description metadata configured as your site requires.