Hugo SEO: baseURL, the embedded Open Graph and Schema partials, the sitemap and redirects
Hugo already outputs static HTML and a sitemap; the head, descriptions and moved pages are the settings to get right.
Published by AI Knows Us · Updated 21 October 2026
Hugo SEO is mostly configuration, because Hugo already produces what search engines like best: plain static HTML files, built in one pass. Out of the box it writes a sitemap.xml, can write a robots.txt, and ships embedded partial templates for Open Graph, X (Twitter) Cards and Schema.org microdata. What it does not do is put them in your head for you, or write your titles and descriptions. The work is to set baseURL correctly, add a description to every page's front matter, call the embedded partials in your base template, handle moved pages with aliases or server redirects, and check the sitemap. This guide follows Hugo's documentation at gohugo.io, read on 22 October 2026, when the latest Hugo release on GitHub was v0.168.0 (13 October 2026).
Why Hugo is a strong base for SEO
Hugo builds every page to an HTML file before anyone visits. A crawler receives the full text in the first response, with no JavaScript to run. Google's JavaScript SEO guide says server-side or pre-rendering "is still a great idea because it makes your website faster for users and crawlers, and not all bots can run JavaScript". Hugo is pre-rendering by design, so the rendering problems that affect script-heavy sites mostly do not apply. Our guide to static content generators compares Hugo with Eleventy, Astro and the rest, and the Astro SEO guide covers the main JavaScript-based alternative.
That leaves the parts that depend on you: the head of every page, URL handling, and content.
Step 1: set baseURL to the live address
baseURL in your project configuration is the root that Hugo uses for permalinks, so it flows into canonical links, the sitemap, Open Graph URLs and aliases. Set it to the exact host you want indexed, including https and www if you use it. A wrong value here produces a sitemap full of URLs Google will not choose.
Hugo also has a canonifyURLs option. Its URL management docs say that, when enabled, Hugo searches the rendered page for site-relative URLs in attributes such as href and src and prepends the baseURL. Most sites do not need it; fix links in templates instead.
Step 2: titles and descriptions in front matter and the base template
Hugo themes vary, so open your base template (often layouts/baseof.html or a head partial) and check what it prints. A sound head has a unique title, a description, and a canonical link built from the page permalink. Every page should carry a description in front matter:
+++
title = 'Hugo SEO checklist'
description = 'The settings that matter for a Hugo site, in order.'
images = ['cover.png']
+++
The description field is used by more than the meta tag. Hugo's embedded Open Graph and Schema templates take the page description first, then fall back to the page summary, then to params.description in your site configuration. Writing it once per page fixes all three. For how to write one that Google keeps, see our guide to meta descriptions.
Step 3: call the embedded SEO partials
Hugo's "Embedded partial templates" page documents ready-made partials. Include them in the head of your base template:
{{ partial "opengraph.html" . }}
{{ partial "schema.html" . }}
{{ partial "twitter_cards.html" . }}
What each one emits, according to the docs:
| Partial | What it writes | Notes from the docs |
|---|---|---|
opengraph.html | og:url, og:site_name, og:title, og:description, locale, article dates, the first 6 tags | Up to 6 og:image tags; uses a series taxonomy for up to 7 og:see_also tags |
schema.html | Schema.org microdata: name, description, datePublished, dateModified, wordCount, images, keywords | This is microdata, not JSON-LD |
twitter_cards.html | X (Twitter) Card tags for link previews | Uses site params and page front matter |
Image handling is the same in both the Open Graph and Schema partials. If images is set in front matter, Hugo uses those. If not, it looks in the page's resources for a file with feature in its name, then cover or thumbnail, and finally uses the first entry in params.images. Naming a page bundle's main image feature.jpg is the low-effort way to get a correct share image on every post.
If you want JSON-LD for rich results, write your own partial; the embedded one does not produce it. To override any embedded partial, the docs say to copy its source into layouts/_partials under the same name.
Step 4: the sitemap
Hugo's sitemap docs say the embedded templates follow version 0.9 of the sitemap protocol. A single-language project gets one sitemap.xml in the root of the public folder. A multilingual project gets a sitemap.xml per language plus a sitemap index at the root.
- Leave a page out with
disable = trueunder[sitemap]in its front matter, for thank-you pages and similar. changefreqis omitted by default. The configuration docs say the default value is empty, so Hugo leaves the field out. You can set it per page or site-wide, but it is optional.- Override the template by creating
layouts/sitemap.xml, or turn sitemap output off withdisableKinds = ['sitemap'].
Step 5: robots.txt
Set enableRobotsTXT = true in your project configuration and Hugo writes a robots.txt from its embedded template, which contains only User-agent: *. The docs say that means permission to crawl everything. To add rules, such as a Sitemap: line, create layouts/robots.txt. Or set the option to false and put a plain robots.txt in the static folder, which Hugo copies to the site root.
Watch for one trap. A staging or preview build that blocks everything in robots.txt must never become the production file.
Step 6: moved pages, aliases and redirects
When you rename a page, list the old paths under aliases in its front matter. Hugo's URL docs describe two ways this works:
- Client-side, the default. Hugo writes a small HTML file at each old path. Its embedded alias template contains a canonical link to the new URL and a
meta http-equiv="refresh"tag that sends the browser on. - Server-side. The docs call this more efficient, because the redirect happens in the HTTP headers before any page content loads. You use the
Aliasesmethod in a template to generate a_redirectsfile (for hosts such as Cloudflare, GitLab Pages and Netlify) or an.htaccessfile (for Apache and LiteSpeed). Then setdisableAliases = trueso Hugo stops writing the HTML files.
If your host supports it, use server-side 301 redirects. Our page on 301 redirects and SEO explains how Google treats each kind.
Questions about Hugo SEO
Is Hugo good for SEO?
Yes. It outputs static HTML that crawlers read in one request, builds a sitemap by default, and includes Open Graph, Schema and X (Twitter) Card templates. You still write titles and descriptions and add the partials to your head.
Does Hugo generate a sitemap automatically?
Yes. A sitemap.xml is written to the public folder on every build, with a sitemap index for multilingual projects. Exclude a page with sitemap.disable in its front matter.
Does Hugo add structured data?
The embedded schema.html partial outputs Schema.org microdata once you include it. For JSON-LD types such as Article or FAQ, write your own partial.
How do I redirect old URLs in Hugo?
Add the old paths to aliases in front matter. By default Hugo writes meta refresh pages; for proper 301s, generate a redirects file for your host and set disableAliases.
Do I need a Hugo SEO theme?
No. Any theme works if its head prints a unique title, description and canonical, and calls the embedded partials. Check your theme's head template before adding a plugin or switching themes.
Fast static pages, and the answer they feed
Static Hugo pages are among the easiest on the web for an AI assistant to fetch and quote, since the whole text is in the first response. That makes your content a likely source. It does not decide whether the assistant names your business when a buyer asks who to choose, and no build log will tell you. Checking that, and which sources the answer leaned on, is the job AI Knows Us was built for.