Every page emits a full head, a structured description of itself, a social card
and an entry in the sitemap. Most of it is written by [Nuxt
SEO](https://nuxtseo.com), which the layer installs as one bundle; the rest is
the part that depends on versions and translations, and that is the layer's own.
One value has to come from you.

## Per page

- **Meta** — the title with the site name appended, the description, and the
  `og:`/`twitter:` pair derived from both. `og:type` and `og:url` are set per
  page; `og:locale` and its alternates come from the locales the site serves.
- **JSON-LD** — one graph per page: a `WebSite` (and your `Organization`, once
  set) declared site-wide, plus a `TechArticle` and a `BreadcrumbList` built from
  the same trail the breadcrumb draws.
- **An Open Graph image**, rendered from the layer's own template — on the
  landing page too. Ship a component of the same name to replace it.
- **`canonical` and `robots`**, which are the version half — see
  [URLs and versions](/concepts/urls-and-versions). The canonical of an old
  version points at the current one, which is why the layer writes it rather
  than letting the module's automatic one stand.
- **A Markdown alternate and its index** — `rel="alternate"
  type="text/markdown"` pointing at this page plus `.md`, and `rel="describedby"`
  pointing at `/llms.txt`. Unlike the canonical, the alternate names the page
  being rendered, so an old version hands over its own source — see
  [Machine readers](/concepts/machine-readers).

The error page is `noindex, follow`: a 404 that gets indexed competes with the
page the reader was looking for, and the suggestions on it are real pages a
crawler should still reach.

## Site-wide

`/sitemap.xml` lists the pages of the default version, per locale.
A non-default version is excluded because its pages carry `noindex` anyway, and
an `eol` version is excluded whether or not it is the default: listing either
asks a crawler to fetch exactly what the page then tells it to drop.
`/robots.txt` is generated beside it.

`duxt.organization` is what names the publisher — see
[Configuration](/reference/configuration). Unset, the site still describes itself
as a `WebSite`; what it loses is the entity behind it, which is what a knowledge
panel or an AI summary reads to credit a source.

## What is checked, and what is only reported

`pnpm check:seo` reads the built pages and fails over a second `canonical`, a
missing `hreflang`, an indexable error page, an `og:locale` in the wrong format,
a graph that does not parse, or a page that does not link to its Markdown
alternate and to `/llms.txt`. It runs in `pnpm check` after the build, because
these tags exist only in rendered HTML.

The link checker reports rather than fails. A link pointing nowhere already fails
the build through the layer's own [build checks](/concepts/build-checks), which
are the ones that understand versions and locale fallbacks; the module's value
here is the devtools panel and the live check while writing.

## The one value you must state

The origin. `hreflang`, `canonical`, the sitemap and the Open Graph images are all
absolute URLs, and a layer cannot know the domain it will be deployed to. Set
`i18n.baseUrl` or `NUXT_PUBLIC_I18N_BASE_URL`; the layer copies it to `site.url`,
where the SEO modules look.

Left unset, each of them degrades to relative output rather than inventing a
domain. That is the deliberate choice: a relative `canonical` still resolves
correctly, and a guessed one is actively wrong — it would point a crawler at
somebody else's site.

## One more variable for a rolling deploy

`NUXT_OG_IMAGE_SECRET` signs the Open Graph image URLs. Without it a secret is
generated per build, which is fine for one instance and wrong for two: a card
whose URL was signed by one instance is rejected by the next, so a link shared
during a rolling deploy loses its image. Generate one once — it does not rotate.