SEO & i18n


Structured data, Open Graph, Twitter cards, and multilingual support

Beautiful Hugo includes comprehensive SEO support (schema.org, Open Graph, Twitter Cards) and full multilingual capabilities via Hugo’s i18n system.

Schema.org Structured Data

The theme automatically generates JSON-LD structured data for every page:

TypeWhere UsedWhat It Contains
WebSiteAll pagesSite name, URL, search action
OrganizationAll pagesOrganization name, logo, URL
WebPageAll pagesPage name, description, breadcrumb
ArticleBlog postsHeadline, author, datePublished, dateModified, publisher, wordCount, timeRequired
Recipetype: recipe pagesIngredients, instructions, times (see Configuration)
ScholarlyArticletype: publication pagesAuthors, periodical, volume, issue, pages, DOI, PDF (see Configuration)
BreadcrumbListAll pagesNavigation hierarchy

No configuration is required — the structured data is generated from your existing hugo.toml settings and page front matter. You can optionally customize the structured data output with the following params:

Custom Structured Data Params

ParamSchema TypeDescription
organizationNameOrganizationOrganization name (defaults to Params.author.name)
organizationLogoOrganizationURL of the organization logo
organizationAddressOrganizationOrganization address (string or structured value)
socialProfilesOrganizationList of social profile URLs for the sameAs field
alternatePageNameWebSiteAlternate name for the site (used as alternateName)
[Params]
  organizationName = "My Company"
  organizationLogo = "https://example.com/logo.png"
  socialProfiles = ["https://twitter.com/example", "https://github.com/example"]
  alternatePageName = "My Company Blog"

Google Scholar Meta Tags

Pages with type: publication emit Highwire Press citation_* meta tags (citation_title, citation_author, citation_publication_date, citation_journal_title, citation_doi, citation_pdf_url, and more). Google Scholar reads these tags to index a paper and link to its PDF. Scholar indexes one paper per page, so each publication needs its own page. See Configuration — Publication Pages.

Open Graph

Open Graph meta tags are generated automatically via Hugo’s built-in internal template:

<meta property="og:title" content="..." />
<meta property="og:description" content="..." />
<meta property="og:type" content="article" />
<meta property="og:image" content="..." />

The og:image tag comes from Hugo’s own cascade: the page images front matter, then the site-wide params.images. The theme cascade below applies to Twitter cards and structured data.

Twitter Cards

Twitter Card meta tags use the summary_large_image card type:

<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@username" />
<meta name="twitter:creator" content="@username" />

The @site and @creator values come from Params.author.twitter.

The card image is resolved from a cascade: page share_img → page image → site share_img → site logo. Set share_img in [Params] to give the whole site a wide default image, because logo is square and crops badly in a summary_large_image card. The Article and Recipe structured data use the same cascade.

Robots Meta Tags

Beautiful Hugo can generate <meta name="robots"> tags from three configuration layers:

  1. hugo.toml — site-wide defaults (only applied to pages in mainSections)
  2. _index.md — section-level overrides
  3. Page front matter — per-page overrides (highest priority)

Each layer overrides matching keys from the previous one.

Boolean Tags

The following tags are supported (set to true to include them):

TagEffect
noindexDo not index this page
nofollowDo not follow links on this page
noneEquivalent to noindex, nofollow
nosnippetDo not show a text snippet or video preview
notranslateDo not offer translation of this page in search results
noimageindexDo not index images on this page
noarchiveDo not show a cached link in search results
nocacheSimilar to noarchive (Bing-specific)
noaiDo not use content for AI training (per dev.ai proposal)
noimageaiDo not use images for AI training

AI Summary Limits

The ai-summary-limit key controls how much of your page content search engines may use for AI-generated summaries:

ValueEffect
noneNo limit (no meta tag emitted)
nosnippetBlock all snippets / AI summaries
0Same as nosnippet
50, 150, 300Character limit for snippets

Site-Wide Configuration

[Params.seo.robots]
  ai-summary-limit = "nosnippet"
  noindex = true
  nofollow = true

[Params.seo.GoogleBot]
  noindex = true
  ai-summary-limit = 50

This produces:

<meta name="robots" content="nosnippet, noindex, nofollow">
<meta name="googlebot" content="noindex, max-snippet:50">

You can add entries for other bots (e.g. Bingbot, DuckDuckBot) the same way.

Per-Page Overrides

Override or supplement site-wide settings in any page’s front matter:

seo:
  robots:
    ai-summary-limit: none
  GoogleBot:
    noindex: false

ExpiryDate

Set ExpiryDate in front matter to add an unavailable_after directive:

---
title: Event Post
expiryDate: 2026-12-31
---

This generates:

<meta name="robots" content="unavailable_after: 2026-12-31T00:00:00+00:00" />

Description Cascade

The page description (used in meta tags and structured data) follows this cascade:

  1. .Description (explicit front matter description field)
  2. .Params.subtitle (the subtitle)
  3. .Summary (auto-generated from content)

Canonical URLs

Every page emits a <link rel="canonical"> tag so search engines know the preferred URL for the content. The URL is resolved in this order:

  1. Front matter canonicalURL — if set, it always wins. Relative values are resolved against the current language’s base URL (so they stay language-correct on multilingual sites); absolute URLs are emitted as-is. Use this when a page intentionally duplicates another and should point search engines at the original:

    ---
    title: Reposted Article
    canonicalURL: https://example.com/the-original/
    ---
    
  2. Paginated list pages (home, sections, taxonomy terms) self-canonicalize. Page two of a sequence points at itself (…/page/2/) rather than at page one, which is Google’s current recommendation for paginated content.

  3. Everything else uses the page’s own permalink.

Disabling the tag

Set disableCanonical = true in your site [Params] to drop the tag site-wide, or in a single page’s front matter to drop it for that page only — for example if you provide canonical links through your own head_custom.html:

[Params]
  disableCanonical = true

Multilingual Support

Beautiful Hugo supports Hugo’s standard multilingual configuration with per-language content directories.

Configuration

DefaultContentLanguage = "en"

[languages]
  [languages.en]
    title = "My Site"
    weight = 1
    contentDir = "content/en"

  [languages.fr]
    title = "Mon Site"
    weight = 2
    contentDir = "content/fr"

Language Switcher

A language switcher appears in the navbar:

  • 2 languages: Inline links next to each other
  • 3+ languages: Dropdown menu

A globe icon marks the switcher, so readers can find it on a page in a language they do not read. Each entry shows the name of its own language in that language, for example Français and 日本語. The theme knows the names of the usual languages, so no configuration is necessary. To use a different name, set languageName for that language:

[languages.fr]
  languageName = "Français (France)"

Supported Languages

Beautiful Hugo ships with translations for 20 languages:

CodeLanguage
enEnglish
deGerman
dkDanish
eoEsperanto
esSpanish
frFrench
hrCroatian
itItalian
jaJapanese
koKorean
lmoLombard
nbNorwegian Bokmål
nlDutch
plPolish
ptPortuguese
pt-brPortuguese (Brazil)
ruRussian
trTurkish
zh-CNChinese (Simplified)
zh-TWChinese (Traditional)

Translation Keys

The i18n files provide translations for common UI strings:

KeyPurpose
postedOnDate“Posted on January 2, 2006”
lastModified“Last modified on …”
readMore“Read more” link text
readingTime“3 min read”
olderPosts / newerPostsPagination labels
previousPost / nextPostPost navigation
pageNotFound404 page text
themeToggleLabelTheme toggle accessibility label
comments“Comments” heading
seeAlso“See also” related posts heading

To add a new language, create a new file in i18n/ (e.g. i18n/pt.yaml for Portuguese) with translations for these keys.

Navbar menu items are automatically translated when the menu entry has an identifier that matches an i18n key with the menu_ prefix. If no matching translation key exists, the menu name from hugo.toml is used as a fallback.

For example, given this menu config:

[[menu.main]]
    identifier = "blog"
    name = "Blog"
    weight = 1

[[menu.main]]
    parent = "blog"
    name = "Posts"
    identifier = "post"
    url = "post/"
    weight = 1

The theme looks up menu_blog and menu_post in the current language’s i18n file. In French (i18n/fr.yaml):

- id: menu_blog
  translation: "Blog"
- id: menu_post
  translation: "Articles"

The theme ships with pre-built translations for common menu items (blog, posts, tags, categories, archives, about, and the example-site section names). You can add your own menu_* keys to your site’s i18n overrides to translate custom menu items.

RSS

When rss = true, an RSS icon appears in the footer and alternate feed links are included in <head>:

[Params]
  rss = true

Hugo auto-generates RSS feeds at /index.xml (site-wide) and /post/index.xml (section-specific).

Google Analytics

Set the tracking ID in your config (loaded only in production):

[Services]
  [Services.googleAnalytics]
    id = "G-XXXXXXXXXX"

Piwik / Matomo

[Params]
  [Params.piwik]
    server = "analytics.example.com"
    id = 1
ParamTypeDescription
piwik.serverstringPiwik/Matomo server hostname
piwik.idstringPiwik/Matomo site ID

Google Custom Search Engine

When gcse is set, a search icon appears in the navbar that opens a search modal:

[Params]
  gcse = "012345678901234567890:abcdefghijk"

The integrated client-side search requires JSON output to function. Ensure your hugo.toml includes the following outputs configuration:

[outputs]
  home = ["HTML", "RSS", "JSON"]
  section = ["HTML", "RSS"]
  page = ["HTML"]

Enable the built-in search UI by configuring a provider:

ParamTypeDefaultDescription
[Params.search] providerstring"fuse"Search provider to use. Set to "none" to disable the built-in search UI.
[Params.search]
  provider = "fuse"

Search Index

The home page JSON output holds the index. Each entry has title, url, excerpt, headings (all headings of the page) and content (the plain text of the page). Fuse gives the title and the headings more weight than the body text.

Pages with the archive or search layout stay out of the index. To keep one more page out, set searchHidden: true in its front matter.

ParamTypeDefaultDescription
searchSectionslistall pagesContent types to index. The entry mainSections expands to Params.mainSections.
searchContentWordsint0Maximum number of words of body text per entry. 0 keeps the full text.
[Params]
  searchSections = ["mainSections", "page"]
  searchContentWords = 200

The index holds the full text of every page by default, which can become large on a big site. The two params above make it smaller: the example site index goes from 138 kB to 48 kB with searchContentWords = 200. Headings keep the results good when the body text is cut.

Search labels come from the theme’s i18n files. To localize or customize labels, override the relevant translation keys in your site, such as searchPlaceholder, searchResultsLabel, searchNoResultsText, searchPrevText, and searchNextText.