What it does

scriptor-sitemap answers /sitemap.xml with a sitemaps.org <urlset> listing every public URL on the site, each with a <lastmod>. Install it and the route starts working: no template edits, no build step, no database table.

Two URL sources are merged into one document:

  • CMS pages — every active page in the Pages category. Nested pages get their full parent-chain path; the empty-slug home page becomes /. lastmod is the page's last-updated timestamp.
  • scriptor-markdown-pages URLsif that plugin is installed (≥ 0.1.9). The dependency is soft: on a site without it, the sitemap is simply pages-only. lastmod is the markdown file's mtime.

This site uses it for /sitemap.xml, which lists both the handful of CMS pages and the whole markdown documentation tree.

How it works

sitemap.xml is not page-shaped — it returns application/xml, not HTML — so the plugin does not try to resolve it as a page. It subscribes to RouteNotFound, the last-chance event Scriptor dispatches before rendering its 404. When the request path matches the configured sitemap path, the plugin emits the XML and ends the request; every other unresolved path falls through to the normal 404 untouched.

Hooking RouteNotFound (rather than a theme's _ext.php) is what keeps the plugin theme-agnostic: it works on any Scriptor install the moment composer discovers it. This is the batteries-included version of the Generate sitemap.xml from PageRepository cookbook recipe — the same short-circuit pattern, plus markdown-pages composition and configuration.

Install

Host install. Not on Packagist, so register the VCS repo, then require:

composer config repositories.scriptor-sitemap \
  vcs https://github.com/bigin/scriptor-sitemap
composer require bigins/scriptor-sitemap:^0.1

Docker. Bake it in with the two build args (typically alongside markdown-pages so its URLs are included):

services:
  scriptor:
    build:
      args:
        SCRIPTOR_PLUGIN_REPOS: "https://github.com/bigin/scriptor-markdown-pages https://github.com/bigin/scriptor-sitemap"
        SCRIPTOR_PLUGINS:      "bigins/scriptor-markdown-pages:^0.1 bigins/scriptor-sitemap:^0.1"

The plugin is auto-discovered (composer type scriptor-plugin); there is nothing to register and no theme line to add.

Configure

All keys are optional, under plugins.sitemap in data/settings/custom.scriptor-config.php:

return [
    'plugins' => [
        'sitemap' => [
            'path'              => '/sitemap.xml',          // route to serve on
            'base_url'          => 'https://example.com',   // canonical <loc> base
            'exclude_templates' => ['search', 'thanks'],    // CMS: drop by template
            'exclude_pagetypes' => ['redirect'],            // CMS: drop by pagetype
            'exclude_ids'       => [42],                    // CMS: drop by page id
            'exclude_slugs'     => ['drafts'],              // CMS: drop by slug
            'exclude_paths'     => ['/private'],            // CMS+markdown: path + children
            'enabled'           => true,                    // turn the route off
        ],
    ],
];
Key Default Effect
path /sitemap.xml Request path the sitemap answers on.
base_url the requested host Absolute base for <loc>. When omitted it is derived from the live request (scheme + Host, honouring an X-Forwarded-Proto proxy).
exclude_templates none CMS pages: drop by template name.
exclude_pagetypes none CMS pages: drop by pagetype.
exclude_ids none CMS pages: drop by page id.
exclude_slugs none CMS pages: drop by slug (the page's own segment, not the full path).
exclude_paths none CMS and markdown URLs: drop by path. An entry matches the exact path or anything beneath it: /private removes /private/ and all children, but not /privacy/.
enabled true Set false to not register the route at all.

With no configuration the plugin serves /sitemap.xml listing every active CMS page plus every markdown URL, using the requested host — the right behaviour for most sites.

Robots

Point crawlers at it from robots.txt:

Sitemap: https://example.com/sitemap.xml