Sitemap, robots.txt and canonical tags generated from your pages automatically.
The technical SEO baseline every site needs and nobody wants to maintain by hand: an XML sitemap built from your published content, a sane robots.txt, and correct canonical + Open Graph tags on every page. When the client publishes or unpublishes in Studio, the sitemap follows.
The CMS already knows every public URL — this plugin teaches your frontend to expose that knowledge. Your agent adds sitemap and robots handlers that read the content API, and a small head-tags helper used by every page template. Nothing is hardcoded: add a content type later and it joins the sitemap by reading the schema.
You are adding sitemap/robots/canonical support to a Prelo project. Read `prelo docs api` first. The site's public origin must come from one place — use an existing `SITE_URL` env/config value or introduce one.
1. **URL inventory helper.** Create a shared server-side helper `getPublicUrls()`:
- Read `GET {CMS_URL}/api/schema` to enumerate types; for each public type the frontend routes (at minimum `page` and `post`), fetch published entries (`per_page=200`, paginate) and map them to absolute URLs using the frontend's actual route patterns.
- Include each entry's `updated_at`/`published_at` for `<lastmod>`.
- Include the homepage and any static routes the frontend defines.
2. **Sitemap.** Serve `GET /sitemap.xml` from the frontend:
- Next.js: `app/sitemap.js` returning the array from `getPublicUrls()`.
- Astro: an `src/pages/sitemap.xml.ts` endpoint rendering the XML.
- One-process Prelo site (`config.site` / `web/site.js`): handle the `/sitemap.xml` path inside `handle(url)` and return `{ html }` with an XML content workaround only if the site module supports content types; otherwise prefer a custom route `"GET /sitemap"` is wrong (custom routes mount under `/api/x/`) — for one-process sites, generate `sitemap.xml` as a static file at build/deploy time instead and serve it from the web root.
- Escape XML entities in URLs.
3. **robots.txt.** Serve `GET /robots.txt`: allow all, plus `Sitemap: {SITE_URL}/sitemap.xml`. If the CMS runs on a separate public hostname, disallow crawling of it entirely there (`/studio`, `/api`, `/uploads` excepted for images if desired).
4. **Head tags helper.** Create `seoTags(entry)` used by every page/post template. It must emit:
- `<title>` and meta description from the entry's `seo` field group when present (`fields.seo.metaTitle` / `metaDescription`), falling back to the entry title and a truncated body excerpt (~155 chars, tag-stripped).
- `<link rel="canonical">` — the entry's `seo.canonical` if set, else the page's own absolute URL. Never emit a canonical pointing at a draft or preview URL.
- `noindex` robots meta when `seo.noindex` is true.
- Open Graph + Twitter card tags: `og:title`, `og:description`, `og:url`, `og:type` (`article` for posts, `website` otherwise), and `og:image` from `seo.ogImage` or the entry's first image field/block, made absolute against `SITE_URL`.
- Wire it into every template; remove any hand-written duplicate tags it replaces.
5. **One H1 discipline.** While editing templates, verify each template renders exactly one `<h1>` (the entry title). Fix any that render more.
6. **Verify.** Run `prelo audit <site-url>` if available (it runs the technical SEO checklist and prints JSON), or manually: fetch `/sitemap.xml` (valid XML, published URLs only), `/robots.txt`, and view-source one page and one post to confirm canonical + OG tags.Nothing for the site owner to do. Publish a page — it's in the sitemap. Set a meta title or the noindex toggle in Studio's SEO fields — the tags follow. Search Console can be pointed straight at /sitemap.xml.
301s editable in Studio, enforced at the edge on deploy.
Contact form with spam protection and email notifications, plus a submissions inbox in Studio.
Filterable gallery with lightbox, fed straight from the media library.