diff --git a/.changeset/cool-ravens-occur.md b/.changeset/cool-ravens-occur.md
new file mode 100644
index 0000000000..fb934ae06f
--- /dev/null
+++ b/.changeset/cool-ravens-occur.md
@@ -0,0 +1,18 @@
+---
+'astro': patch
+---
+
+Add user-configurable `sitemapFilter` option.
+
+This option can be used to ensure certain pages are excluded from your final sitemap.
+
+```ts
+// astro.config.ts
+import type { AstroUserConfig } from 'astro'
+
+const config: AstroUserConfig = {
+ sitemap: true,
+ sitemapFilter: (page: string) => !page.includes('secret-page')
+}
+export default config
+```
diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts
index 975afd1923..de2449104c 100644
--- a/packages/astro/src/@types/astro.ts
+++ b/packages/astro/src/@types/astro.ts
@@ -226,6 +226,25 @@ export interface AstroUserConfig {
*/
sitemap?: boolean;
+ /**
+ * @docs
+ * @name buildOptions.sitemapFilter
+ * @type {undefined|((page: string) => boolean)}
+ * @default `undefined`
+ * @description
+ * Customize sitemap generation for your build by excluding certain pages.
+ *
+ * ```js
+ * {
+ * buildOptions: {
+ * sitemap: true
+ * sitemapFilter: (page) => !page.includes('secret-page')
+ * }
+ * }
+ * ```
+ */
+ sitemapFilter?: (page: string) => boolean
+
/**
* @docs
* @name buildOptions.pageUrlFormat
diff --git a/packages/astro/src/core/build/index.ts b/packages/astro/src/core/build/index.ts
index 0e5a50f223..816abd8bad 100644
--- a/packages/astro/src/core/build/index.ts
+++ b/packages/astro/src/core/build/index.ts
@@ -149,7 +149,8 @@ class AstroBuilder {
// Build your final sitemap.
if (this.config.buildOptions.sitemap && this.config.buildOptions.site) {
timer.sitemapStart = performance.now();
- const sitemap = generateSitemap(pageNames.map((pageName) => new URL(pageName, this.config.buildOptions.site).href));
+ const sitemapFilter = this.config.buildOptions.sitemapFilter ? (this.config.buildOptions.sitemapFilter as (page: string) => boolean) : undefined;
+ const sitemap = generateSitemap(pageNames.map((pageName) => new URL(pageName, this.config.buildOptions.site).href), sitemapFilter);
const sitemapPath = new URL('./sitemap.xml', this.config.dist);
await fs.promises.mkdir(new URL('./', sitemapPath), { recursive: true });
await fs.promises.writeFile(sitemapPath, sitemap, 'utf8');
diff --git a/packages/astro/src/core/config.ts b/packages/astro/src/core/config.ts
index 5f5ca042e1..e20bc4132d 100644
--- a/packages/astro/src/core/config.ts
+++ b/packages/astro/src/core/config.ts
@@ -50,6 +50,7 @@ export const AstroConfigSchema = z.object({
.string()
.optional()
.transform((val) => (val ? addTrailingSlash(val) : val)),
+ sitemapFilter: z.function().optional(),
sitemap: z.boolean().optional().default(true),
pageUrlFormat: z
.union([z.literal('file'), z.literal('directory')])
diff --git a/packages/astro/src/core/render/sitemap.ts b/packages/astro/src/core/render/sitemap.ts
index 4f695253d4..2956d72ace 100644
--- a/packages/astro/src/core/render/sitemap.ts
+++ b/packages/astro/src/core/render/sitemap.ts
@@ -1,14 +1,19 @@
+
const STATUS_CODE_PAGE_REGEXP = /\/[0-9]{3}\/?$/;
/** Construct sitemap.xml given a set of URLs */
-export function generateSitemap(pages: string[]): string {
+export function generateSitemap(pages: string[], filter?: (page: string) => boolean): string {
// TODO: find way to respect URLs here
- // TODO: find way to exclude pages from sitemap
// copy just in case original copy is needed
// make sure that 404 page is excluded
// also works for other error pages
- const urls = [...pages].filter((url) => !STATUS_CODE_PAGE_REGEXP.test(url));
+ let urls = [...pages].filter((url) => !STATUS_CODE_PAGE_REGEXP.test(url));
+
+ if (filter) {
+ urls = urls.filter(url => filter(url));
+ }
+
urls.sort((a, b) => a.localeCompare(b, 'en', { numeric: true })); // sort alphabetically so sitemap is same each time
let sitemap = ``;
for (const url of urls) {