Fix Your Sitemap for Astro

Updated April 2026·By SitemapFixer Team

Astro uses the @astrojs/sitemap integration to emit sitemap-index.xml and sitemap-0.xml at build time. Misconfigured site URLs, missing getStaticPaths entries, and SSR routes commonly cause empty or incomplete sitemaps.

Analyze your Astro sitemap nowTry Sitemap Fixer Free

Astro sites break sitemap generation in a handful of predictable ways: hybrid rendering (some static, some SSR), dynamic routes with incomplete getStaticPaths, and Content Collections where drafts leak through. The integration is simple on the surface but has sharp edges once you mix rendering modes.

Debugged an Astro 4 site with Content Collections a few weeks back. 340 markdown posts, sitemap listing 47. The issue: their [...slug].astro page was reading from a collection filtered at render time, but getStaticPaths pulled from a separate (stale) export. After aligning the two, all 340 showed up.

Common Astro Sitemap Issues

Working astro.config.mjs

import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';

export default defineConfig({
  site: 'https://example.com',
  trailingSlash: 'never',
  integrations: [
    sitemap({
      filter: (page) =>
        !page.includes('/admin') &&
        !page.includes('/preview') &&
        !page.includes('/draft'),
      customPages: [
        // SSR routes you want indexed
        'https://example.com/dashboard-public',
      ],
      i18n: {
        defaultLocale: 'en',
        locales: { en: 'en-US', de: 'de-DE', fr: 'fr-FR' },
      },
      serialize(item) {
        if (item.url.includes('/blog/')) {
          item.changefreq = 'weekly';
          item.priority = 0.7;
        }
        return item;
      },
    }),
  ],
});

Content Collections and drafts

Astro Content Collections don't have a native "draft" flag - you add it to your schema:

// src/content/config.ts
import { defineCollection, z } from 'astro:content';

const blog = defineCollection({
  schema: z.object({
    title: z.string(),
    pubDate: z.date(),
    draft: z.boolean().default(false),
  }),
});

export const collections = { blog };

// In [...slug].astro
export async function getStaticPaths() {
  const posts = await getCollection('blog', ({ data }) => !data.draft);
  return posts.map(p => ({ params: { slug: p.slug }, props: { post: p } }));
}

The filter in getCollection drops drafts from prerendered pages, which means they never enter the sitemap in the first place. Much cleaner than filtering at the sitemap level.

i18n with hreflang

Pass the i18n option to the sitemap integration and it emits xhtml:link alternates automatically - as long as your routes actually exist for each locale. If /de/about doesn't exist but /en/about does, the sitemap still lists only /en/about. Translation gaps show up as missing alternates in GSC's International Targeting report.

Hybrid rendering gotchas

If you set output: 'server' globally, every route becomes SSR and your sitemap comes out empty. Use output: 'hybrid' and mark specific pages as export const prerender = true for anything you want indexed. Then add any remaining SSR URLs through customPages.

Step-by-Step Fix Guide

  1. Install with npm install @astrojs/sitemap and add it to integrations
  2. Set site: 'https://yourdomain.com' in astro.config.mjs
  3. Pass a filter callback excluding admin, preview, and draft routes
  4. For dynamic routes, make getStaticPaths exhaustive. For SSR routes to index, use customPages
  5. Add i18n with defaultLocale and locales to emit hreflang
  6. Add draft to your Content Collection schema and filter in getCollection
  7. Run npm run build, inspect dist/sitemap-index.xml, verify URL count matches expectations
  8. Verify with curl https://yoursite.com/sitemap-index.xml and submit to Google Search Console

Frequently Asked Questions

Why is my Astro sitemap empty?
The most common cause is the missing site field in astro.config.mjs. Without it, @astrojs/sitemap can't build absolute URLs and skips generation silently. Set site to your production origin and rebuild.
Does @astrojs/sitemap include SSR routes?
No. The integration only knows about routes prerendered at build time. For SSR routes that should be indexed, pass their URLs through the customPages option in the sitemap config.
Where does Astro output the sitemap?
dist/sitemap-index.xml and dist/sitemap-0.xml after astro build. Ensure your deploy pipeline uploads the full dist/ directory, not just HTML.
Analyze your Astro sitemap
Find all issues in your sitemap - free, no credit card needed
Analyze My Sitemap Free
Other platform guides