Hugo title-cases your tags everywhere (and how to stop it)
Tags in my frontmatter are lowercase:
tags = ["aws", "eks", "headscale"]
Tags in the rendered HTML came out title-cased — in three different places:
<!-- OpenGraph meta -->
<meta property="article:tag" content="Aws" />
<!-- Schema.org microdata -->
<meta itemprop="keywords" content="Aws,Eks,Headscale">
<!-- Tag links on the post page and blog index -->
<a href="/til/aws/">#Aws</a>
What doesn’t work
# hugo.toml
pluralizeListTitles = false
capitalizeListTitles = false
These settings only affect list page titles (like /tags/aws/ rendering as “aws” instead of “Aws” in the heading). They have zero effect on OpenGraph meta, microdata, or tag links.
The cause
Hugo title-cases tags in three separate places, each from a different template:
_internal/opengraph.html— calls.Titleon each tag forarticle:tagmeta_internal/schema.html— calls.Titleon each tag foritemprop="keywords"meta- Theme templates —
single.htmlandlist.htmluse.LinkTitleor.Page.Titlewhen rendering tag links
No config setting overrides any of these. They’re hardcoded in Hugo’s source and in the theme templates.
The fix: three template overrides
1. OpenGraph + schema.org: layouts/partials/seo_tags.html
Most themes include Hugo’s internal templates via a partial. Override it at the project level to use raw tag values:
<!-- OpenGraph tags — use raw {{ . }} instead of .Title -->
{{ range .Params.tags }}<meta property="article:tag" content="{{ . }}">
{{ end }}
<!-- Schema.org keywords — use raw $tag instead of .Title -->
{{ if .Params.tags }}<meta itemprop="keywords" content="{{ range $i, $tag := .Params.tags }}{{ if $i }},{{ end }}{{ $tag }}{{ end }}">{{ end }}
Replace both {{ template "_internal/opengraph.html" . }} and {{ template "_internal/schema.html" . }} with inline versions that use the raw tag strings.
2. Post tag links: layouts/_default/single.html
The theme iterates with .GetTerms "tags" and renders .LinkTitle, which Hugo title-cases. Override with:
{{ range .Params.tags }}
<a href="{{ printf "/til/%s/" (. | urlize) }}">#{{ . }}</a>
{{ end }}
3. Blog index tag links: layouts/_default/list.html
The theme iterates with .Site.Taxonomies.tags and renders .Page.Title, which Hugo also title-cases. Override with:
{{ range $tag, $_ := .Site.Taxonomies.tags }}
<a href="{{ printf "/til/%s/" ($tag | urlize) }}">#{{ $tag }}</a>
{{ end }}
Result
<meta property="article:tag" content="aws">
<meta itemprop="keywords" content="aws,eks,headscale">
<a href="/til/aws/">#aws</a>
Lowercase everywhere. Three overrides for one config setting that should exist but doesn’t.