Skip to main content

Optimize

SEO

Auto-generated sitemap.xml + robots.txt + canonical URLs, plus per-page frontmatter controls for indexing, Open Graph, and Twitter cards. Zero plugins, zero build config.

NookDocs emits the entire on-page SEO surface by default: sitemap, robots, canonical URLs, Open Graph tags, Twitter cards, <link rel="icon">. You control crawler behaviour from nookdocs.config.json#seo globally, and from each page's frontmatter individually.

What's auto-generated

Every tenant exposes these on the docs origin with zero configuration:

URLWhat it is
/sitemap.xmlXML sitemap with every non-hidden page, lastmod from last git commit
/robots.txtUser-agent: * rules honouring seo.indexing; references the sitemap
/llms.txtLLM-crawler index (see llms.txt)
/favicon.ico etc.Served from seo.favicon or your brand config
Per-page <link rel="canonical">Always the absolute tenant URL
Per-page Open Graph + Twitter tagsFrom page frontmatter with fallback to seo.* defaults

Try it on this site: /sitemap.xml, /robots.txt, /llms.txt.

Global seo configuration

seo.titlestringpath

Default site title. Page frontmatter title wins when present. Final format: {page title} | {seo.title}.

seo.descriptionstringpath

Default meta description used when a page has no frontmatter description.

seo.keywordsstring[]path

Default meta keywords for the site. Per-page frontmatter keywords (an array) overrides. Emitted as <meta name="keywords">.

seo.authorstringpath

Default content author. Emitted as <meta name="author"> and the article author. Per-page frontmatter author overrides.

seo.ogImagestringpath

Default Open Graph image (1200×630 PNG recommended). Applied to og:image and twitter:image. Per-page og.image frontmatter wins.

seo.indexing'default' | 'noindex'pathdefault: default

noindex emits a robots: noindex, nofollow meta tag on every page + writes Disallow: / into /robots.txt. Use for staging sites and private docs.

seo.metatagsRecord<string, string>path

Free-form meta-tag map. Each key/value becomes <meta name="KEY" content="VALUE">. Common keys: twitter:site, theme-color, color-scheme, robots.

seo.faviconstringpath

Favicon path (SVG preferred). Renders as <link rel="icon"> globally. Falls back to brand config favicon if unset.

{
  "seo": {
    "title": "Acme Documentation",
    "description": "Build, deploy, and scale with Acme.",
    "keywords": ["acme api", "acme sdk", "acme docs"],
    "author": "Acme, Inc.",
    "ogImage": "/og-default.png",
    "indexing": "default",
    "favicon": "/favicon.svg",
    "metatags": {
      "twitter:site": "@acme",
      "theme-color": "#0ea5e9"
    }
  }
}

Per-page frontmatter controls

Every page's MDX frontmatter can override site-wide defaults or add richer metadata:

---
title: Getting started with Acme
description: Create your first Acme workspace in under 2 minutes.
keywords: [acme, workspace, getting started]
hidden: false
sitemap: true
og:
  image: /images/getting-started-og.png
  title: Ship docs in 2 minutes
---
  • title + description populate <title>, <meta name="description">, og:title, og:description, twitter:title, twitter:description.

  • keywords adds <meta name="keywords"> (low SEO impact but still honoured).

  • hidden: true drops the page from sitemap, llms.txt, and search indexing. Page still renders at its URL — hiding it from discovery is intentional for gated flows.

  • sitemap: false excludes the page from the sitemap while still rendering it.

  • og.image / og.title — per-page Open Graph overrides for social link previews.

See frontmatter reference for the full field list.

Canonical URLs

Every page ships a <link rel="canonical"> pointing at the absolute tenant URL. Custom domains (docs.acme.com) are preferred over the default subdomain (acme.nookdocs.site) when a primary domain is set. This prevents duplicate-content penalties when a tenant has multiple verified domains.

To override the canonical target per-page, use seo.metatags.canonical in your config or a page-level override (planned). For now the default is correct for 99% of cases.

noindex strategy

Three levers, with increasing scope:

  1. Per-page — frontmatter hidden: true drops the page from sitemap + llms.txt + search. Page still renders for anyone with the URL.

  2. Site-wide (staging)seo.indexing: "noindex" emits <meta name="robots" content="noindex, nofollow"> site-wide and Disallow: / in robots.txt. Google and Bing respect within 24-48 hours.

  3. Protocol-level — block crawlers entirely via reverse proxy / WAF. Only for truly private docs.

Open Graph checklist

A sharable docs page needs:

  • Page-specific og:title and og:description (frontmatter title + description give you both)

  • 1200×630 PNG at og:image (per-page og.image or seo.ogImage default)

  • twitter:card: summary_large_image — set via seo.metatags or defaults

  • Canonical URL (automatic)

Test with Slack, Discord, Twitter, LinkedIn after your first deploy — each one caches for ~7 days so set it right before sharing widely.

Limitations

  • No per-page canonical override in frontmatter yet. Planned. For now, seo.metatags.canonical sets it site-wide (rarely what you want).

  • No structured-data / JSON-LD emission. BreadcrumbList, TechArticle, and FAQPage schema.org types are on the FAZ 9 roadmap.

  • No Core Web Vitals audit output. Measure via Lighthouse / PageSpeed Insights manually for now.

Related

Was this page helpful?

Last updated August 11, 2026