---
title: Publish a sitemap
description: "Expose an auto-built /sitemap.xml that enumerates every canonical URL, skips drafts and redirects, and uses front-matter dates for lastmod."
canonical_url: https://usepennington.net/how-to/feeds/sitemap/
sidecar_url: https://usepennington.net/how-to/feeds/sitemap.md
content_hash: sha256:436506ceed49aa20c247e27301d49fab41a07db75e3117e377a2e93fb738a6d7
tokens: 1135
uid: how-to.feeds.sitemap
reading_time_minutes: 3
---

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](https://usepennington.net/tutorials/getting-started/first-site.md).

 
## Before you begin

 
 - A working Pennington site (see [Create your first Pennington site](https://usepennington.net/tutorials/getting-started/first-site.md) 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](https://usepennington.net/how-to/content-services/custom-content-service.md), [Source content from a remote API](https://usepennington.net/how-to/content-services/source-from-a-remote-api.md)) 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/`
 
 
## Related

 
 - Reference: [SitemapService](https://usepennington.net/reference/api/sitemap-service.md)
 - How-to: [Publish an RSS feed](https://usepennington.net/how-to/feeds/rss.md)
 - How-to: [Configure redirects](https://usepennington.net/how-to/pages/redirects.md)
 
 
[Previous
                
                Publish a custom feed from a content service](https://usepennington.net/how-to/feeds/custom-feed.md)[Next
                    
                Generate social card images](https://usepennington.net/how-to/feeds/social-cards.md)