
Astroでブログを作ったけど、RSSフィードって自分で用意しないといけないの?

作れたとしても、記事の全文まで配信するものなのかな…?
ブログを公開したあとに出てくるのが、更新をどうやって知らせるかという問題です。
SNSで流すのが当たり前になった今、RSSフィードは役目を終えた仕組みに見えるかもしれません。
ただフィードは、読者が自分の意思で登録してくれる数少ないルートです。
タイムラインの表示順に左右されず、出したものがそのまま並ぶのは、個人ブログと相性がいいと思います。
Astroには公式パッケージの @astrojs/rss があるので、XMLを手で書く必要はありません。
この記事では導入から rss.xml の書き方、そしていちばん迷いやすい「概要だけ送るか、本文まで送るか」の判断までを順番に整理していきます。
- @astrojs/rss を入れてフィードを出すまでの手順
itemsに渡せる項目と、下書きの除外・新着順への並べ替え- 概要配信と全文配信の違いと、MDXのブログで気をつけたいこと
- フィードの場所を読者に伝える方法と、
customData・stylesheetの役割
同じ「一覧を配るファイル」でも、サイトマップは検索エンジン向け、フィードは読者向けという違いがあります。あちらの手順は別記事にまとめています。

- Astro 7.3系(最新は7.3.3)と @astrojs/rss 4.0系(最新は4.0.19)を対象にした内容です
- パッケージマネージャは pnpm で書いていますが、npm・yarn でもコマンドを読み替えれば同じです
公式の解説は RSS(Astro Docs) にあります。
RSSフィードとは?ブログの更新を「購読」してもらう仕組み
RSSフィードは、サイトの更新内容を決まった書式で並べたファイルです。
記事のタイトル・公開日・URL・概要などが、機械が読める形で入っています。
読者はこのファイルのURLをフィードリーダーに登録しておきます。
あとはリーダーが定期的に中身を取りに来て、新着があれば手元に並べてくれる、という流れです。
雑誌でいえば定期購読に近い仕組みですね。毎回サイトを見に行かなくても、更新のほうから届きます。
登録したサイトのフィードをまとめて取りに行き、新着を一覧で見せてくれるアプリやサービスのことです。
ブラウザの拡張機能として動くもの、スマホアプリとして使うものなど、種類はいろいろあります。
古い仕組みに見えて、更新チェックの手段としては今も使われています。
@astrojs/rssでフィードを作る手順
@astrojs/rss は、Astroが公式に出しているフィード生成用のパッケージです。
サイトマップのように integrations へ足すインテグレーションではなく、自分で作ったエンドポイントの中から呼び出す関数を提供してくれます。
やることは、パッケージの追加・site の設定・エンドポイントの作成・ビルドでの確認の4つです。
1. パッケージを入れる
まずはパッケージを追加します。
pnpm add @astrojs/rss
2. site に公開URLを入れる|無いとビルドが止まる
次に、astro.config.mjs でサイトの公開URLを設定します。
// astro.config.mjs
import { defineConfig } from "astro/config";
export default defineConfig({
site: "https://example.com",
});
フィードに載る記事のリンクは、この値と記事のパスを組み合わせて作られます。
site がないと正しいURLを組み立てられないので、先に入れておきましょう。
書き忘れたときの挙動もはっきりしています。
rss() は受け取ったオプションを検証するので、site が無いとエラーを投げてビルドごと止まります。
[RSS] Invalid or missing options:
Invalid input: expected string, received undefined (site)
ここはサイトマップと対照的なところです。
@astrojs/sitemap は site を忘れても警告を出すだけで先に進み、サイトマップの生成がこっそりスキップされます。
RSSはその場で止まるぶん、気づかないまま公開してしまうことはないとも言えます。
らるじゅ同じ site でも、忘れたときの扱いはパッケージごとに違うんですよね。
一度書いておけば両方で効くので、Astroでサイトを公開するなら最初に済ませておくのが楽だと思います。
3. src/pages/rss.xml.js を作る
フィードの本体は、src/pages の中に置いたエンドポイントで作ります。
GET という名前の関数をエクスポートすると、そのファイル名のURLでレスポンスが返る仕組みです。
まずは記事を1件だけ手で書いて、動く形を作ってみます。
// src/pages/rss.xml.js
import rss from "@astrojs/rss";
export function GET(context) {
return rss({
title: "ブログのタイトル",
description: "ブログの説明文",
site: context.site,
items: [
{
title: "最初の記事",
description: "記事の概要文が入ります。",
pubDate: new Date("2026-09-21"),
link: "/blog/first-post/",
},
],
});
}
rss() に渡した title と description はフィード全体の名前と説明で、リーダーの一覧にそのまま出ます。
site には context.site を渡して、astro.config.mjs に書いた値をそのまま使う形です。
TypeScriptで書きたい場合は、ファイル名を rss.xml.ts にしても同じように動きます。
4. ビルドして確認する
エンドポイントを書いたら、ビルドして中身を見てみます。
pnpm build
できあがるのは dist/rss.xml です。エディタで開いて、タイトルと記事が入っているかを確認します。
ここで見ておきたいのが、記事の件数と、リンクが https:// から始まる絶対URLになっているかです。
リンクは site と記事のパスから組み立てられるので、公開URLを変えたときはここも見ておくと安心です。
フィードに何を載せるか|itemの項目と並び順
形ができたら、次は実際の記事を流し込みます。
このとき効いてくるのが、items に渡す項目と、その並び順です。
itemに渡せる基本の項目
items の各要素には、次の項目を入れられます。
| 項目 | 内容 |
|---|---|
title | 記事のタイトル。description があれば省略できる |
description | 記事の概要。title があれば省略できる |
link | 記事のURL |
pubDate | 記事の公開日 |
content | 記事の本文(HTML) |
title と description は、どちらか一方は必須です。
残りは任意ですが、リンクと公開日がないとリーダー側で扱いづらいので、基本は入れておくと考えてよいと思います。
記事をコンテンツコレクションで管理しているなら、getCollection で取り出して items に変換できます。
// src/pages/rss.xml.js
import rss from "@astrojs/rss";
import { getCollection } from "astro:content";
export async function GET(context) {
const posts = await getCollection("blog");
return rss({
title: "ブログのタイトル",
description: "ブログの説明文",
site: context.site,
items: posts.map((post) => ({
title: post.data.title,
description: post.data.description,
pubDate: post.data.pubDate,
link: `/blog/${post.id}/`,
})),
});
}
気をつけたいのがリンクの作り方です。
Astro v5以降、エントリの識別子は post.slug ではなく post.id になりました。
古い解説をそのまま貼ると、ここが undefined の混ざったURLになってしまいます。
コンテンツコレクション側の書き方は別記事で整理しているので、あわせてどうぞ。

下書きを混ぜない
draft は、コンテンツコレクションのスキーマに自分で足したただの項目です。
Astroが自動で外してくれるものではありません。
除外したいなら、getCollection の第2引数でフィルタをかけます。
const posts = await getCollection("blog", ({ data }) => !data.draft);
これで draft: true の記事はフィードに出なくなります。
サイト側で下書きを隠していても、フィードのコードに同じ処理を入れ忘れるとフィードにだけ下書きが並ぶことになるので、ここは忘れずに。
新しい順に並べる
並び順も自動ではありません。取得した配列をそのまま渡すと、狙った順番になるとは限らないんですよね。
新着順にしたいなら、pubDate で降順に並べ替えます。
const posts = (await getCollection("blog", ({ data }) => !data.draft)).sort(
(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
);
フィードリーダーは新しいものから見せる作りが多いので、新着が先頭に来る形にしておくのが無難です。
概要配信と全文配信、どちらにする?
ここまでで記事を並べるところまではできました。
最後に決めるのが、本文をどこまで届けるかです。
RSSでは、description に概要だけを入れる形と、content に本文のHTMLまで入れる形のどちらも選べます。
前者を概要配信、後者を全文配信と呼びます。
全文配信にする場合、公式のレシピでは Markdown をHTMLに変換してからサニタイズして渡します。
そのために2つのパッケージを追加します。
pnpm add markdown-it sanitize-html
そのうえで、post.body をHTMLにして content に入れます。
// src/pages/rss.xml.js
import rss from "@astrojs/rss";
import { getCollection } from "astro:content";
import MarkdownIt from "markdown-it";
import sanitizeHtml from "sanitize-html";
const parser = new MarkdownIt();
export async function GET(context) {
const posts = await getCollection("blog");
return rss({
title: "ブログのタイトル",
description: "ブログの説明文",
site: context.site,
items: posts.map((post) => ({
title: post.data.title,
pubDate: post.data.pubDate,
link: `/blog/${post.id}/`,
content: sanitizeHtml(parser.render(post.body), {
allowedTags: sanitizeHtml.defaults.allowedTags.concat(["img"]),
}),
})),
});
}
parser.render() で本文をHTMLに変え、sanitizeHtml() で余計なタグを落としています。
既定の許可タグに img を足しているのは、そのままだと画像が消えてしまうからです。
ここで押さえておきたい制限があります。
公式が明記しているとおり、この方法ではMDXのコンポーネントやJSXの式は処理されません。
post.body はMDXの生のテキストで、markdown-it は既定でHTMLのタグをそのまま出力しない設定になっています。
そのため <PickupBox> も <Balloon> も、タグの文字列のまま本文に残ることになります。
リーダーに届く本文には、<PickupBox type="point" title="…"> のような書き出しがそのまま文字として混ざり、中の文章だけが地の文に続く形になります。
{1 + 1} のようなJSXの式も同じで、計算されずに文字のまま出ます。
つまり読者の手元には、書いたつもりのない本文が届いてしまいます。
| 観点 | 概要だけ配信 | 全文配信 |
|---|---|---|
| 追加で入れるもの | ◎ @astrojs/rss だけ | △ markdown-it・sanitize-html が必要 |
| MDXのコンポーネント | ◎ 影響を受けない | × タグが文字のまま残る |
| リーダーでの読み心地 | △ 続きはサイトを開いてもらう | ◎ リーダーの中で読み切れる |
| サイトへの誘導 | ◎ 本文はサイトで読んでもらえる | △ 開いてもらいにくくなる |
| 実装の見通し | ◎ 項目を渡すだけ | ○ サニタイズの設定を考える必要がある |
- MDXでコンポーネントを使っているなら、概要配信。本文はサイトで読んでもらう形が安全
- 素のMarkdownだけで書いているなら、全文配信も選べる。リーダーの中で読み切ってもらえる
- 迷うならまず概要配信で出す。あとから
contentを足すのは難しくない
らるじゅ全文配信は読者にとってはうれしい形ですが、届いた本文が崩れていたら逆効果です。
自分のブログがどこまで素のMarkdownで書けているか、確かめてから決めるのが良さそうです。
読者にフィードの場所を伝える
フィードは、作っただけでは気づいてもらえません。
リーダー側から見つけられるようにするところまでがセットです。
headに自動検出用のリンクを入れる
多くのフィードリーダーは、サイトのURLを入れると <head> を見てフィードを探します。
そのための1行がこれです。
<link rel="alternate" type="application/rss+xml" title={SITE_TITLE} href={new URL("rss.xml", Astro.site)} />
rel="alternate" と type="application/rss+xml" の組み合わせが目印になります。
href は Astro.site から組み立てているので、ここでも site の設定が効いてきます。
共通のheadコンポーネントを持っているなら、そこに1行足すだけで全ページに入ります。
リーダーに登録して確かめる
最後は、実際にフィードリーダーへ登録して見え方を確認するのが確実です。
- 新しい記事が先頭に来ているか
- 記事のリンクから本文のページを開けるか
- 下書きが混ざっていないか
- 全文配信にした場合、本文が崩れていないか
このブログもAstroで動いていて、/rss.xml でフィードを配信しています。
見え方が気になる方は、お使いのリーダーで試してみてください。
customDataとstylesheetで整える
rss() には、フィードを細かく調整するためのオプションもあります。
return rss({
title: "ブログのタイトル",
description: "ブログの説明文",
site: context.site,
items: [],
customData: `<language>ja</language>`,
stylesheet: "/rss/styles.xsl",
trailingSlash: false,
});
それぞれの役割は次のとおりです。
| オプション | 内容 |
|---|---|
customData | フィードに生のXMLを足す。<language>ja</language> のような要素を入れる |
stylesheet | ブラウザで開いたときの見た目を整えるXSLのパス。絶対パスで指定する |
trailingSlash | URL末尾のスラッシュの扱い。設定で trailingSlash: "never" にしているなら false にして揃える |
stylesheet は、フィードのURLをブラウザで直接開いた人向けの設定です。
指定しなければXMLがそのまま表示されるだけなので、見た目を整えたくなったときに用意すれば十分だと思います。
まとめ|RSSフィードは「どこまで送るか」を決めてから作る
@astrojs/rss を使えば、Astroのサイトでもフィードの配信ができます。
必要なのはパッケージの追加と site の設定、そして src/pages にエンドポイントを1つ置くことだけです。
手を動かす量より悩むのは、本文をどこまで送るかのほうだと思います。
MDXでコンポーネントを使っているブログなら、まずは概要配信から始めるのが扱いやすいはずです。
- @astrojs/rss は
pnpm add @astrojs/rssで追加し、src/pages/rss.xml.jsのGETから呼び出す siteを設定していないとrss()がエラーを投げ、ビルドごと止まるitemsにはtitle・description・link・pubDate・contentを渡せる(titleとdescriptionはどちらか必須)- 下書きの除外も新着順の並べ替えも自動ではないので、
getCollectionのフィルタとsortで自分で行う - 全文配信ではMDXのコンポーネントやJSX式が処理されないため、MDX中心のブログは概要配信が無難
- headの
<link rel="alternate">を入れて、リーダーから見つけてもらえるようにする
らるじゅフィードは一度置いてしまえば、あとは記事を書くだけで勝手に更新されていく仕組みです。
まずは概要配信で出してみて、リーダーに登録して眺めるところから始めてみてください!
