all articles
dev · ZH

如何让自己的博客支持 RSS

May 10, 2026·9 min read·by Mr Panda·2 阅读

如何让自己的博客支持 RSS

很多人搭好了博客,写了几篇文章,却忽略了一个基础功能:RSS。

RSS(Really Simple Syndication)是一种标准化的内容订阅协议。读者通过 RSS 阅读器订阅你的博客后,每次你发布新文章,阅读器会自动拉取更新——不需要读者主动访问你的网站,不需要你去社交媒体上吆喝。

在算法推荐主导信息分发的时代,RSS 反而成了一种稀缺能力:让读者以自己的方式、按自己的节奏消费内容。对创作者来说,RSS 订阅者通常是最忠实的读者群体,因为他们是主动选择关注你的。

这篇文章从 RSS 的原理讲起,手把手教你给博客加上 RSS 支持。无论你用的是静态站点生成器还是自己写的博客系统,都能找到对应的方案。

RSS 的工作原理

RSS 的核心是一个 XML 文件。你把这个文件托管在博客的某个固定 URL 上(通常是 /feed.xml/rss.xml),RSS 阅读器定期请求这个地址,解析 XML 内容,就能知道你发了什么新文章。

一个最简的 RSS 文件长这样:

<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>我的博客</title>
    <link>https://example.com</link>
    <description>关于技术和思考的个人博客</description>
    <language>zh-CN</language>
    <lastBuildDate>Sat, 10 May 2026 08:00:00 +0800</lastBuildDate>

    <item>
      <title>第一篇文章</title>
      <link>https://example.com/posts/first</link>
      <description>这是文章的摘要或全文内容。</description>
      <pubDate>Sat, 10 May 2026 08:00:00 +0800</pubDate>
      <guid>https://example.com/posts/first</guid>
    </item>

  </channel>
</rss>

结构非常清晰:<channel> 描述博客本身的信息,每个 <item> 对应一篇文章。读者的 RSS 阅读器拿到这个文件后,会根据 <guid> 判断哪些文章是新的,然后展示给用户。

RSS 2.0 vs Atom

目前主流的订阅格式有两种:RSS 2.0 和 Atom。两者功能几乎相同,主要区别在于 Atom 的规范更严格、对内容类型的支持更好。大多数 RSS 阅读器同时兼容两种格式,选哪个都可以。本文以 RSS 2.0 为例,因为它的结构更直观。

关键字段说明

<channel> 层级:

字段 是否必须 说明
title 博客名称
link 博客首页 URL
description 博客的一句话介绍
language 内容语言,如 zh-CN
lastBuildDate Feed 最后更新时间
atom:link 推荐 Feed 自身的 URL(自引用)

<item> 层级:

字段 是否必须 说明
title 文章标题
link 文章 URL
description 文章摘要或全文 HTML
pubDate 推荐 发布时间,RFC 822 格式
guid 推荐 全局唯一标识,通常用文章 URL
category 文章分类标签

各类博客的 RSS 实现方案

Hugo

Hugo 原生支持 RSS,默认就会在构建时生成 Feed。你要做的只是确认配置正确。

hugo.toml(或 config.toml)中添加:

[outputs]
  home = ["HTML", "RSS"]

[params]
  description = "关于技术和思考的个人博客"
  author = "你的名字"

Hugo 会自动在 /index.xml 生成 RSS Feed。如果你想自定义 Feed 的路径和内容,可以创建模板文件 layouts/_default/rss.xml

{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>{{ .Site.Title }}</title>
    <link>{{ .Site.BaseURL }}</link>
    <description>{{ .Site.Params.description }}</description>
    <language>{{ .Site.LanguageCode }}</language>
    <lastBuildDate>{{ now.Format "Mon, 02 Jan 2006 15:04:05 -0700" }}</lastBuildDate>
    <atom:link href="{{ .Permalink }}" rel="self" type="application/rss+xml" />
    {{- range first 20 .Site.RegularPages }}
    <item>
      <title>{{ .Title }}</title>
      <link>{{ .Permalink }}</link>
      <description>{{ .Summary | html }}</description>
      <pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" }}</pubDate>
      <guid>{{ .Permalink }}</guid>
    </item>
    {{- end }}
  </channel>
</rss>

Hexo

Hexo 需要安装插件。在项目目录下执行:

npm install hexo-generator-feed --save

然后在 _config.yml 中添加配置:

feed:
  enable: true
  type: rss2
  path: feed.xml
  limit: 20
  content: true       # 是否在 Feed 中包含文章全文
  content_limit: 300   # 摘要截取字数(content 为 false 时生效)

运行 hexo generate 后,Feed 会自动生成在 /feed.xml

Next.js

Next.js 没有内置的 RSS 支持,需要自己生成。推荐在构建阶段用脚本生成静态 XML 文件。

安装依赖:

npm install rss

创建生成脚本 scripts/generate-rss.mjs

import { writeFileSync } from 'fs';
import RSS from 'rss';
import { getAllPosts } from '../lib/posts.mjs';

const feed = new RSS({
  title: '我的博客',
  site_url: 'https://example.com',
  feed_url: 'https://example.com/feed.xml',
  language: 'zh-CN',
});

const posts = getAllPosts();

posts.forEach((post) => {
  feed.item({
    title: post.title,
    url: `https://example.com/posts/${post.slug}`,
    description: post.summary,
    date: new Date(post.date),
  });
});

writeFileSync('./public/feed.xml', feed.xml({ indent: true }));
console.log('RSS feed generated.');

package.json 中把这个脚本加到构建流程里:

{
  "scripts": {
    "build": "node scripts/generate-rss.mjs && next build"
  }
}

如果你用的是 App Router,也可以通过 Route Handler 动态生成:

// app/feed.xml/route.ts
import { getAllPosts } from '@/lib/posts';

export async function GET() {
  const posts = getAllPosts();

  const xml = `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>我的博客</title>
    <link>https://example.com</link>
    <description>关于技术和思考的个人博客</description>
    <language>zh-CN</language>
    ${posts
      .map(
        (post) => `
    <item>
      <title>${escapeXml(post.title)}</title>
      <link>https://example.com/posts/${post.slug}</link>
      <description>${escapeXml(post.summary)}</description>
      <pubDate>${new Date(post.date).toUTCString()}</pubDate>
      <guid>https://example.com/posts/${post.slug}</guid>
    </item>`
      )
      .join('')}
  </channel>
</rss>`;

  return new Response(xml, {
    headers: {
      'Content-Type': 'application/xml',
      'Cache-Control': 'public, max-age=3600',
    },
  });
}

function escapeXml(str: string): string {
  return str
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;');
}

Jekyll

Jekyll 和 Hugo 类似,生态中有成熟的插件。在 Gemfile 中添加:

gem "jekyll-feed"

_config.yml 中启用:

plugins:
  - jekyll-feed

title: 我的博客
description: 关于技术和思考的个人博客
url: https://example.com

构建后会自动在 /feed.xml 生成 RSS Feed。GitHub Pages 默认支持这个插件,不需要额外配置。

纯手写博客

如果你的博客是纯手写的 HTML 页面,没有用任何框架,可以写一个简单的脚本在每次发布时重新生成 feed.xml

用 Python 实现:

import os
import xml.etree.ElementTree as ET
from datetime import datetime
from email.utils import format_datetime

def generate_feed(posts, output_path='feed.xml'):
    rss = ET.Element('rss', version='2.0')
    channel = ET.SubElement(rss, 'channel')

    ET.SubElement(channel, 'title').text = '我的博客'
    ET.SubElement(channel, 'link').text = 'https://example.com'
    ET.SubElement(channel, 'description').text = '关于技术和思考的个人博客'
    ET.SubElement(channel, 'language').text = 'zh-CN'

    for post in posts:
        item = ET.SubElement(channel, 'item')
        ET.SubElement(item, 'title').text = post['title']
        ET.SubElement(item, 'link').text = post['url']
        ET.SubElement(item, 'description').text = post['summary']
        ET.SubElement(item, 'pubDate').text = format_datetime(post['date'])
        ET.SubElement(item, 'guid').text = post['url']

    tree = ET.ElementTree(rss)
    ET.indent(tree, space='  ')
    tree.write(output_path, encoding='unicode', xml_declaration=True)


posts = [
    {
        'title': '第一篇文章',
        'url': 'https://example.com/posts/first',
        'summary': '这篇文章讲了什么',
        'date': datetime(2026, 5, 10, 8, 0, 0),
    },
]

generate_feed(posts)

把这个脚本加到你的发布流程中,每次写完新文章时运行一次即可。

让 RSS Feed 被发现

生成了 Feed 文件还不够,你需要告诉浏览器和爬虫"这个网站有 RSS"。

在 HTML 的 <head> 中添加一行:

<link rel="alternate" type="application/rss+xml"
      title="我的博客" href="/feed.xml" />

这行标签的作用是:当用户使用支持 RSS 自动发现的浏览器插件(如 Feedbro、RSSHub Radar)访问你的网站时,插件能自动检测到 Feed 地址并提示订阅。

另外,在网站的页脚或导航栏放一个 RSS 图标链接也很有必要。很多读者习惯通过视觉提示来判断一个网站是否支持 RSS。

全文输出还是摘要输出

生成 RSS Feed 时需要做一个决定:<description> 里放文章全文还是只放摘要?

全文输出的好处是读者体验好——打开阅读器就能读完,不用跳转到你的网站。这对读者来说是最方便的。缺点是 Feed 文件体积会比较大,而且你无法统计文章的实际阅读量。

摘要输出的好处是读者必须点击链接才能阅读全文,你可以通过网站统计工具掌握真实的访问数据。缺点是多了一步跳转,部分读者会因为嫌麻烦而跳过。

实际的选择取决于你的优先级。如果你更看重读者体验和传播效率,选全文;如果你需要精确的流量数据或者网站本身有广告等商业化需求,选摘要。

一种折中方案是输出前 300-500 字的内容作为"丰富摘要",既给了足够的信息让读者判断是否值得阅读,又保留了引导点击的动机。

验证和调试

Feed 写完后,用以下工具验证格式是否正确:

  • W3C Feed Validation Servicevalidator.w3.org/feed/)——输入你的 Feed URL,它会检查 XML 格式和字段是否符合规范,并给出详细的错误提示。
  • 直接在浏览器中打开 Feed URL——大多数浏览器会以原始 XML 的形式展示内容,检查一下结构是否完整、中文是否乱码。

几个常见的问题:

日期格式错误。 RSS 2.0 的 <pubDate> 要求使用 RFC 822 格式,如 Sat, 10 May 2026 08:00:00 +0800。ISO 8601 格式(2026-05-10T08:00:00+08:00)在 RSS 2.0 中是无效的,但在 Atom 格式中是标准写法。

XML 特殊字符未转义。 文章标题或摘要中如果包含 &<> 等字符,必须转义为 &amp;&lt;&gt;,否则整个 XML 会解析失败。最安全的做法是用 <![CDATA[...]]> 包裹内容:

<description><![CDATA[
  <p>这篇文章讲了 A & B 的关系,以及为什么 X < Y。</p>
]]></description>

编码声明缺失。 XML 文件的第一行应该声明编码方式:<?xml version="1.0" encoding="UTF-8"?>。中文内容必须确保文件实际保存为 UTF-8 编码。

进阶:自动化与持续集成

如果你的博客部署在 Vercel、Netlify 或 GitHub Pages 上,Feed 的生成可以完全自动化。

以 GitHub Pages + GitHub Actions 为例,你可以在 .github/workflows/deploy.yml 中添加 Feed 生成步骤:

- name: Generate RSS Feed
  run: python scripts/generate_feed.py

- name: Deploy to GitHub Pages
  uses: peaceiris/actions-gh-pages@v3
  with:
    github_token: ${{ secrets.GITHUB_TOKEN }}
    publish_dir: ./public

每次推送新文章到仓库,CI 会自动重新生成 Feed 并部署。你只需要专注于写作本身。

最后

给博客加上 RSS 是一件投入产出比很高的事情。实现成本低——多数情况下就是一个 XML 文件加几行配置,维护成本也低——生成流程可以完全自动化。但它给读者提供了一种干净、稳定、不被算法干扰的内容获取方式。

如果你正在经营个人博客,RSS 值得作为一个基础设施来对待,而不只是一个可有可无的附加功能。

━━━ fin ━━━

If you read this far — thank you.
Come tell me what you thought on X.