This documentation is also published as Markdown for efficient machine reading: the whole site is indexed at /llms.txt, and every page has a clean Markdown copy at the same URL with .md appended. These are generated from the same source and cost far fewer tokens to read than this rendered HTML.

Skip to main content Skip to navigation
Guides

Publish a sitemap

Expose an auto-built /sitemap.xml that enumerates every canonical URL, skips drafts and redirects, and uses front-matter dates for lastmod.

/sitemap.xml is registered and served automatically on every AddPennington-based host; a working site already emits one. The options below tune what crawlers see — absolute <loc> values, draft/redirect exclusion, or turning the sitemap off on BlogSite. For a first site, start with Create your first Pennington site.

Before you begin

  • A working Pennington site (see Create your first Pennington site if not)
  • Pages using an IFrontMatter implementation — DocFrontMatter, BlogFrontMatter, or a custom one — so IsDraft and (optionally) Date flow through to the sitemap builder
  • A known publishing target: either a fully-qualified URL (set CanonicalBaseUrl) or a sub-path via dotnet run -- build --base-url /sub/ (the sitemap falls back to OutputOptions.BaseUrl)

Options

Set CanonicalBaseUrl so <loc> values resolve

When CanonicalBaseUrl is set on PenningtonOptions, DocSiteOptions, or BlogSiteOptions, the sitemap builder prefixes every URL with it — typically https://your-domain.com/ — producing the absolute <loc> entries crawlers require. Without it, entries fall back to the build's --base-url value or to /.

csharp
new BlogSiteOptions
{
    CanonicalBaseUrl = "https://example.com",
    // ...
}

Exclude drafts and redirects with front matter

The sitemap drops any page whose front matter has isDraft: true or sets redirectUrl:. search: false and llms: false are not honored — those are client-side UX preferences, not SEO directives, so opting a page out of search does not remove it from the sitemap.

Every other discovered HTML route is included, regardless of how it is sourced — markdown, Razor pages, and the routes that custom content services (Source content from outside the markdown pipeline, Source content from a remote API) and AddTaxonomy term pages emit all appear. The only routes left out are those with no canonical HTML page: redirects (which serve a 30x) and llms.txt-only sidecars. Non-HTML outputs such as JSON feeds and generated data files are skipped because their output file is not .html.

(BlogSite only) Suppress the endpoint with EnableSitemap = false

On an AddBlogSite host, set BlogSiteOptions.EnableSitemap = false to skip the /sitemap.xml MapGet entirely — useful when the host environment owns its own sitemap. The flag forwards into PenningtonOptions.MapSitemap; on bare AddPennington or AddDocSite, set that property directly to opt out.

csharp
new BlogSiteOptions
{
    EnableSitemap = false,
    // ...
}

Result

/sitemap.xml returns a <urlset> with one <url> per non-draft, non-redirect page, with absolute <loc> values when CanonicalBaseUrl is set:

xml
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://example.com/</loc>
    <lastmod>2024-01-15</lastmod>
  </url>
  <url>
    <loc>https://example.com/how-to/feeds/sitemap/</loc>
    <lastmod>2024-02-03</lastmod>
  </url>
</urlset>

Verify

  • Run dotnet run and fetch /sitemap.xml. Expect a <urlset> document with one <url><loc>…</loc></url> per non-draft, non-redirect page
  • Mark a page isDraft: true or set redirectUrl: on it and refetch. That URL is absent from the <urlset>
  • Publish with CanonicalBaseUrl = "https://example.com" and confirm every <loc> starts with https://example.com/. Omit it and run dotnet run -- build /sub/ to see <loc> values start with /sub/