SEO for Solos
0.14xstatic

Hugo SEO

A compiled static site generator with no JavaScript runtime at all. Most of the performance group passes by construction, and everything is Go template logic over front matter.

What you start with

  • +Zero JavaScript unless you add it
  • +Build times measured in milliseconds even at thousands of pages
  • +Built-in sitemap and RSS output formats
  • +Front matter available to templates as typed page variables

What it makes harder

  • !The default sitemap template stamps every page with the same date unless the front matter carries lastmod
  • !There is no server, so redirects and security headers are entirely the host's job
  • !safeJS and safeHTML disable escaping, which is required for JSON-LD and dangerous everywhere else

Recipes

Each one is real Hugo code, not the same snippet with the imports swapped.

A sitemap with real per-page dates

A sitemap claiming 2,100 pages changed at 03:14 this morning teaches the crawler the field is noise. It then stops reading it, and you lose the signal permanently. Check crawl-lastmodified-differentiated

layouts/_default/sitemap.xml
{{ printf "<?xml version=\"1.0\" encoding=\"utf-8\"?>" | safeHTML }}
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  {{ range .Pages }}
  <url>
    <loc>{{ .Permalink }}</loc>
    {{/* .Lastmod falls back to the build time when front matter has no date.
         Checking .IsZero first is what stops a build-stamped sitemap. */}}
    {{ if not .Lastmod.IsZero }}<lastmod>{{ .Lastmod.Format "2006-01-02T15:04:05-07:00" }}</lastmod>{{ end }}
    <priority>{{ if .IsHome }}1.0{{ else if eq .Section "playbook" }}0.9{{ else }}0.6{{ end }}</priority>
  </url>
  {{ end }}
</urlset>

Canonical from the site baseURL

A relative canonical resolves differently depending on the requested URL, which is exactly the ambiguity a canonical exists to remove. Check meta-canonical-absolute

layouts/partials/head.html
<title>{{ .Title }} | {{ .Site.Title }}</title>
<meta content="{{ .Description | default .Site.Params.description }}" name="description" />
{{/* .Permalink is absolute and built from baseURL, so the origin is defined
     once in config.toml and nowhere else. */}}
<link href="{{ .Permalink }}" rel="canonical" />
<meta content="{{ .Permalink }}" property="og:url" />
<meta content="summary_large_image" name="twitter:card" />

JSON-LD with safeJS

Multiple disconnected script tags describe several unrelated things. One graph describes one page, and lets nodes reference each other instead of repeating themselves. Check schema-single-script

layouts/partials/jsonld.html
{{ $graph := dict
  "@context" "https://schema.org"
  "@graph" (slice
    (dict "@type" "Organization" "@id" (printf "%s#organization" .Site.BaseURL) "name" .Site.Title)
    (dict "@type" "WebSite" "@id" (printf "%s#website" .Site.BaseURL) "url" .Site.BaseURL
          "publisher" (dict "@id" (printf "%s#organization" .Site.BaseURL)))
  )
}}
{{/* safeJS is required or Go templates escape the JSON into uselessness. */}}
<script type="application/ld+json">{{ $graph | jsonify | safeJS }}</script>

Gating by content files rather than routes

An ungated dynamic route generates a page for any slug anyone types, including slugs an attacker generates. That is how a site accidentally publishes ten thousand thin pages. Check pseo-entity-gate

content/glossary/canonical-url.md
---
title: "Canonical URL"
slug: "canonical-url"
lastmod: 2026-07-27
category: "foundations"
related: ["duplicate-content", "trailing-slash"]
---

A canonical URL is the address a site declares to be the authoritative version
of a page.

The same checks in other frameworks

Questions

What does Hugo give you before you write any SEO code?

Hugo starts with: Zero JavaScript unless you add it; Build times measured in milliseconds even at thousands of pages; Built-in sitemap and RSS output formats; Front matter available to templates as typed page variables.

What does Hugo make harder?

The default sitemap template stamps every page with the same date unless the front matter carries lastmod This is specific to Hugo rather than a general SEO problem.

How many SEO recipes does SEO for Solos publish for Hugo?

SEO for Solos publishes 4 Hugo recipes, each with real Hugo code and a note on what differs from the same check in other frameworks.