Fix Your Sitemap for Hugo
Hugo ships a default sitemap.xml template, but taxonomies, section pages, future-dated posts, and multilingual mounts quickly create a sitemap that doesn't match what you actually want indexed.
Hugo's sitemap gets it mostly right by default, but "mostly right" includes every taxonomy term page, every pagination URL, and sometimes drafts. The fix is template-level: override layouts/sitemap.xml and filter what gets emitted.
Reviewed a Hugo docs site last month. 420 content pages, 1,800 URLs in the sitemap. The padding was every tag page (290), every author page (14), and every pagination URL for the blog section (/blog/page/2 through /blog/page/47). A 20-line template override cut the sitemap to 435 URLs.
Common Hugo Sitemap Issues
- Taxonomy list pages (
/tags/,/categories/) inflating the sitemap with thin aggregates - Draft and future-dated posts included when building with
--buildDraftsor--buildFuture - Pagination URLs (
/page/2/) treated as unique pages - Multilingual builds creating one sitemap per language without a parent index
- Missing
lastmodbecauseenableGitInfois off and.Lastmodfalls back to.Date _index.mdsection pages emitted but accidentally filtered out by a custom template- Default priority of 0.5 on everything - Google ignores it, but it confuses humans reading the file
- Sitemap published at
/sitemap.xmlbut robots.txt pointing at/sitemap_index.xml(or vice versa)
hugo.toml sitemap config
baseURL = 'https://example.com/'
enableGitInfo = true
disableKinds = ['taxonomy', 'term'] # drop tag/category list pages
[sitemap]
changefreq = 'weekly'
filename = 'sitemap.xml'
priority = 0.5
[languages]
[languages.en]
weight = 1
languageName = 'English'
contentDir = 'content/en'
[languages.de]
weight = 2
languageName = 'Deutsch'
contentDir = 'content/de'
defaultContentLanguage = 'en'Custom layouts/sitemap.xml
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
{{ range where .Data.Pages "Params.sitemap_exclude" "ne" true }}
{{ if not (in .RelPermalink "/page/") }}
<url>
<loc>{{ .Permalink }}</loc>
{{ if not .Lastmod.IsZero }}
<lastmod>{{ safeHTML (.Lastmod.Format "2006-01-02T15:04:05-07:00") }}</lastmod>
{{ end }}
{{ with .Sitemap.ChangeFreq }}
<changefreq>{{ . }}</changefreq>
{{ end }}
{{ if ge .Sitemap.Priority 0.0 }}
<priority>{{ .Sitemap.Priority }}</priority>
{{ end }}
{{ if .IsTranslated }}
{{ range .AllTranslations }}
<xhtml:link rel="alternate" hreflang="{{ .Lang }}" href="{{ .Permalink }}"/>
{{ end }}
{{ end }}
</url>
{{ end }}
{{ end }}
</urlset>This version skips anything with sitemap_exclude: true in front matter and any pagination URL. The xhtml:link block emits hreflang alternates for translated content.
Multilingual sitemap index
With multiple languages configured, Hugo generates /en/sitemap.xml, /de/sitemap.xml, etc. Add a layouts/sitemapindex.xml template so you have a single URL to submit to Google Search Console. Some Hugo versions need an explicit output format declaration in hugo.toml under [outputs] to actually render the index - check with hugo list --output if the file isn't appearing in public/.
Deployment pipeline notes
Production builds should be plain hugo or hugo --minify. Never ship with --buildDrafts or --buildFuture - that's how draft posts leak into live sitemaps. If you use a preview environment, put it on a separate domain with its own robots.txt that blocks everything. Netlify and Vercel preview deploys are usually fine because the preview URL is randomized and not linked to your production domain.
Step-by-Step Fix Guide
- Set
enableGitInfo = trueinhugo.tomlso.Lastmodreflects actual commit times - Disable unused kinds (
disableKinds = ['taxonomy', 'term']) if tag/category pages shouldn't exist - Create
layouts/sitemap.xmland filter onsitemap_excludeand pagination URLs - Add
sitemap_exclude: trueto thank-you pages, legal notices, and any near-duplicate landing pages - Never run
--buildDraftsor--buildFuturein production - For multilingual, set
[languages]withweightanddefaultContentLanguage, and addlayouts/sitemapindex.xml - Deploy, verify with
curl https://yourdomain.com/sitemap.xml | grep -c "<url>" - Submit to Google Search Console and monitor coverage