コンテンツへスキップ
My Site

RSS and Sitemap

The blog template ships with both an RSS feed and a sitemap pre-configured. This page explains how they work and how to customize them.

Sitemap

The sitemap is generated by @astrojs/sitemap. It automatically includes every statically generated page.

Setup

Install the integration (already included in the blog template):

pnpm add @astrojs/sitemap

Add to astro.config.mjs:

import sitemap from "@astrojs/sitemap";

export default defineConfig({
  site: "https://your-site.com",   // Required for sitemap
  integrations: [
    notro(),
    sitemap(),
  ],
});

The sitemap is generated at /sitemap-index.xml and linked from the <head> automatically.

Excluding pages

To exclude specific pages from the sitemap:

sitemap({
  filter: (page) => !page.includes("/draft/"),
}),

RSS feed

The RSS feed is served from /rss.xml and is generated by @astrojs/rss.

Setup

Install the package (already included in the blog template):

pnpm add @astrojs/rss

Create src/pages/rss.xml.ts:

import rss from "@astrojs/rss";
import { getCollection } from "astro:content";
import { getSortedPosts, excludeFixedPages } from "@/lib/posts";
import { config } from "@/config";
import type { APIContext } from "astro";

export async function GET(context: APIContext) {
  const allPosts = await getCollection("posts");
  const posts = getSortedPosts(excludeFixedPages(allPosts));

  return rss({
    title: config.site.name,
    description: config.site.description,
    site: context.site!,
    items: posts.map((post) => ({
      title: post.data.title,
      description: post.data.description,
      pubDate: post.data.date ? new Date(post.data.date) : new Date(),
      link: `/blog/${post.data.slug}/`,
    })),
    customData: `<language>ja</language>`,
  });
}

Linking the feed

Add the RSS feed link to your <head> in Layout.astro:

<link
  rel="alternate"
  type="application/rss+xml"
  title={config.site.name}
  href={new URL("rss.xml", Astro.site)}
/>

Including content in the feed

To include the full HTML content of each post in the RSS feed, use sanitizeHtml and marked:

pnpm add sanitize-html marked

import sanitizeHtml from "sanitize-html";

import { marked } from "marked";

items: posts.map((post) => ({

  title: post.data.title,

  pubDate: post.data.date ? new Date(post.data.date) : new Date(),

  link: `/blog/${post.data.slug}/`,

  content: sanitizeHtml(await marked.parse(post.data.markdown)),

})),

Note: The RSS content is rendered from raw markdown, not from the compiled MDX with Notion components. Custom block types (callouts, toggles, etc.) will appear as plain text in feed readers.


robots.txt

Create public/robots.txt to control crawler access:

User-agent: *
Allow: /

Sitemap: https://your-site.com/sitemap-index.xml

Open Graph and SEO

The blog template's Layout.astro includes Open Graph meta tags automatically:

<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:url" content={Astro.url} />
<meta property="og:type" content="article" />

Set site in astro.config.mjs to ensure og:url is an absolute URL.

Per-post OG image

To add per-post Open Graph images, generate them using Astro's @vercel/og or satori:

pnpm add @vercel/og

Create src/pages/og/[slug].png.ts and pass the resulting URL as og:image in your layout. Refer to the Astro docs for details on dynamic image generation.