Your prerender filter doesn't gate your sitemap
By Warya Wayne ·
TanStack Start can prerender your pages and generate a sitemap. Both are configured on the same plugin, a few lines apart, and it is extremely natural to assume the filter on one applies to the other.
It does not.
tanstackStart({
prerender: {
enabled: true,
filter: (page) => !page.path.startsWith('/api'),
},
sitemap: { enabled: true, host: 'https://example.com' },
})Every page you filtered out is still in sitemap.xml.
Where the lists come from
The sitemap builder doesn't look at prerender results. It builds from the discovered pages list, and the only thing that removes an entry is a per-page flag:
pages.filter((page) => page.sitemap?.exclude !== true)So prerender.filter and sitemap.exclude are two independent gates. A page can be skipped by the renderer and still advertised to Google — which is the worst of both, because now you're pointing crawlers at something you deliberately chose not to build.
Two ways I hit this
A duplicate URL. With autoStaticPathsDiscovery on, route-based discovery contributed /blog/ while link crawling contributed /blog. Same document, two sitemap entries, and one of them 307-redirects to the other. Filtering the trailing-slash form out of prerendering changed nothing in the sitemap. I turned auto-discovery off and let link crawling produce one canonical form per page.
The RSS feed. Adding <a href="/rss.xml">rss</a> to the footer meant the crawler discovered the feed as a page. Excluding it from prerendering was not enough — /rss.xml still appeared in the sitemap as an indexable URL.
That one needs the actual flag:
pages: [
{ path: '/' },
{ path: '/rss.xml', sitemap: { exclude: true } },
],The trap inside the fix
Declaring pages replaces the discovery seed rather than adding to it. My first attempt listed only /rss.xml, and the build cheerfully prerendered zero pages and wrote an empty sitemap. You have to list / explicitly so link crawling still has somewhere to start.
Check the artifact, not the log
The build log said Prerendered 11 pages in every one of these broken states. It was accurate and completely useless — the defect was in a file the log never mentions.
A sitemap is a promise to a crawler about what exists. Read the generated XML before you ship it:
grep -o '<loc>[^<]*</loc>' dist/client/sitemap.xmlEvery URL in there should return 200, not a redirect, and should be something you actually want indexed.