RSSHub 解决的是一个很具体的问题:你想订阅的网站没有 RSS。
B 站的 UP 主动态、小红书的博主更新、微博、知乎专栏、各种论坛的板块 —— 这些站点本身不提供订阅源。RSSHub 把它们统统转成标准 RSS,扔进阅读器里就能像看博客一样追更。
它有个官方公共实例 rsshub.app,很多人直接用它。但用久了会遇到三件事:
- 限流。 请求太频繁会被拒,尤其是热门路由。
- 被墙。 公共实例在国内访问不稳定,时好时坏。
- 路由失效。 某个平台改了页面结构,公共实例上的对应路由就挂了,你只能等别人修。
自建之后这些都不是问题 —— 你控制自己的实例,可以调缓存、配 Cookie、走代理。
这两个名字像,作用完全是两回事:
| RSSHub | FreshRSS | |
|---|---|---|
| 干什么 | 生成 RSS(把网站转成订阅源) | 阅读 RSS(订阅、分类、标记已读) |
| 有没有界面 | 基本没有,纯接口 | 有完整的阅读界面 |
| 访问方式 | URL 即订阅源 | 浏览器 / 手机 App |
RSSHub 的输出是一个个 RSS 地址,你还得有个阅读器去消费它们。
站内那篇 FreshRSS 自托管阅读器讲的是后半段。这篇讲前半段。两个都自建,就是一套完全属于自己的信息流,不依赖任何第三方服务。
只想先跑通一个的话,先装 FreshRSS(有界面,能直接看到效果),再补 RSSHub。
RSSHub 本身很轻 —— 它是 Node.js 写的,不吃 CPU,空载内存占用也就一两百 MB。但有两种情况会让它变得很重。
第一是浏览器路由。 有些网站是纯前端渲染的,普通 HTTP 请求拿不到内容,RSSHub 得开一个无头 Chromium 去跑页面。Chromium 一开,单个会话就是两三百 MB 内存,并发几个直接上 G。
第二是把 Chromium 塞进 RSSHub 容器。 官方有两个镜像:
| 镜像 | 体积 | 浏览器路由 |
|---|---|---|
diygod/rsshub | 约 500MB | ❌ |
diygod/rsshub:chromium-bundled | 约 1.2GB | ✅ |
配置建议:
| 配置 | 能干什么 |
|---|---|
| 1 核 512MB | 只能跑轻装版,浏览器路由必挂 |
| 1 核 1GB | 勉强,浏览器路由容易超时 |
| 1 核 2GB | 推荐起点,浏览器路由能用 |
| 2 核 4GB | 多人共用、路由开得多 |
内存不够的机器跑 chromium-bundled,典型症状是请求一直转圈最后超时,因为 Chromium 起了但内存不够渲染。这种情况加 Swap 能缓解一些(低内存 VPS 优化里有 Zram 和 Swap 的配置),但根本上还是得加内存。
硬盘方面留 20GB 以上 —— 镜像本身就要 1GB 多,加上 Redis 数据和 Docker 的存储层,空间消耗比想象中快。定期清一下 Docker 镜像,站内这篇 Docker 磁盘清理有用。
如果你要订阅的站点都提供正常 HTML(博客、新闻站、部分论坛),一个命令就够了:
docker run -d \
--name rsshub \
--restart unless-stopped \
-p 127.0.0.1:1200:1200 \
-e NODE_ENV=production \
-e CACHE_EXPIRE=600 \
diygod/rsshub
打开 http://你的IP:1200,能看到 RSSHub 的欢迎页就说明跑起来了。
注意端口绑的是 127.0.0.1。 RSSHub 默认没有任何访问控制,一旦暴露在公网,任何人发现你的 IP 都能拿它当免费爬虫用 —— 用你的服务器、你的 IP 去抓别人的站。轻则被目标站封 IP,重则收到投诉。
需要浏览器路由的话,用 Compose 跑三个服务:
services:
rsshub:
image: diygod/rsshub
container_name: rsshub
restart: unless-stopped
ports:
- "127.0.0.1:1200:1200"
environment:
NODE_ENV: production
CACHE_TYPE: redis
REDIS_URL: "redis://redis:6379/"
PUPPETEER_WS_ENDPOINT: "ws://browserless:3000"
CACHE_EXPIRE: 600
CACHE_CONTENT_EXPIRE: 3600
REQUEST_TIMEOUT: 10000
REQUEST_RETRY: 2
ACCESS_KEY: 换成一串长随机字符
depends_on:
- redis
- browserless
redis:
image: redis:alpine
restart: unless-stopped
volumes:
- redis-data:/data
browserless:
image: browserless/chrome
restart: unless-stopped
environment:
- MAX_CONCURRENT_SESSIONS=2
volumes:
redis-data:
这个组合的思路是把浏览器拆出去单独跑:RSSHub 用标准镜像(500MB),需要渲染页面时通过 WebSocket 连到 browserless 那个容器,由它开 Chromium。好处是 RSSHub 容器保持轻量,浏览器进程崩了也不影响主服务。
MAX_CONCURRENT_SESSIONS=2 是并发上限,这是控制内存的关键参数。2GB 的机器设 2,4GB 可以设 3-4,设太高会 OOM。
另一种做法是用 chromium-bundled 镜像,不跑 browserless。 少一个服务,配置更简单,但 Chromium 和 RSSHub 共享同一个容器内存,互相挤。除非你机器内存很宽裕,否则还是拆开更好。
RSSHub 的缓存参数值得单独说,因为它直接关系到你的 IP 会不会被封。
| 变量 | 默认值 | 说明 |
|---|---|---|
CACHE_TYPE | memory | 缓存方式,可选 memory / redis,留空则禁用 |
CACHE_EXPIRE | 300 秒 | 路由缓存时间 |
CACHE_CONTENT_EXPIRE | 3600 秒 | 抓到的内容缓存多久 |
MEMORY_MAX | 256 | memory 缓存的最大条数 |
默认 300 秒意味着每 5 分钟就会重新去源站抓一次。 如果你的阅读器有 20 个 RSSHub 订阅,那就是每 5 分钟对目标站发起 20 次请求 —— 这个频率足以触发对方的反爬。
把 CACHE_EXPIRE 调到 600-1800 秒,对绝大多数内容源来说完全够用(新闻也不会 5 分钟更新一次),而抓取频率直接降到原来的三分之一到六分之一。
多订阅源的话务必用 Redis 缓存。 memory 缓存存在进程内,容器重启就没了;Redis 能持久化,而且多个实例可以共享。
上面提到 RSSHub 默认无鉴权,ACCESS_KEY 就是官方给的解法:
environment:
ACCESS_KEY: 你的长随机密钥
设好之后,所有请求都要带上密钥才响应:
https://rss.example.com/bilibili/user/dynamic/123456?key=你的密钥
从 / 和 /robots.txt 是例外,不带 key 也能访问(方便你确认服务活着)。
这带来一个副作用:订阅 URL 变得很长。 每个源后面都要挂 ?key=...。好在 FreshRSS、Inoreader 这些阅读器都能存完整 URL,复制粘贴一次就行。
还有个更精细的做法:如果你想把某些源分享给别人,又不想暴露主密钥,可以用 ?code= 参数 —— 它是对「路由路径 + 主密钥」做 MD5 得到的值,只能访问那一个路由:
https://rss.example.com/some/route?code=md5哈希值
除了 ACCESS_KEY,反代再上一层认证更稳。 用 Caddy 加 Basic Auth 是很省事的做法:
rss.example.com {
basicauth {
rss $2a$14$你的密码哈希
}
reverse_proxy 127.0.0.1:1200
}
但要注意:加了 Basic Auth 之后,阅读器必须支持在 URL 里带认证信息。FreshRSS 支持这种格式:
https://用户名:密码@rss.example.com/路由?key=xxx
不确定你的阅读器支不支持的话,就只用 ACCESS_KEY,别叠 Basic Auth。
Caddy 的配置细节看这篇 Caddy 反向代理完全指南。
自建能躲开公共实例的限流,但躲不开平台本身的反爬。国内几个平台是重灾区:
B 站会校验 Cookie,没配的话动态路由返回 {"code":-401,"message":"非法访问"}。配置方式是在环境变量里加上你的 B 站 UID 和 Cookie:
BILIBILI_COOKIE_你的UID=你的Cookie
等号两边不要有空格,UID 是你自己的数字 UID。
小红书更严格,需要:
XIAOHONGSHU_COOKIE="abRequestId=xxx;web_session=xxx"
Cookie 从浏览器开发者工具里复制,注意它会过期 —— 通常几周到几个月,失效了要重新抓一次然后重启容器。这是自建 RSSHub 最主要的长期维护成本。
还有个更麻烦的情况:你的服务器 IP 被平台拉黑了。 尤其是便宜云服务商的 IP 段,早就被各大平台标记过。这种情况下配 Cookie 也没用,得走代理:
PROXY_URI=socks5h://用户名:密码@代理地址:端口
RSSHub 会通过这个代理去抓取。代价是多一层延迟,而且代理本身要花钱。
一个务实的混合策略: 能正常抓的源用自己的实例,抓不动的(比如某些国内平台)继续用公共实例或者第三方实例。别为了一个路由折腾半天。
RSSHub 本身没有阅读界面,配好之后要在阅读器里订阅。
以 FreshRSS 为例,添加订阅时把 URL 里的域名换掉:
# 公共实例的写法
https://rsshub.app/bilibili/user/dynamic/2267573
# 换成你自己的
https://rss.example.com/bilibili/user/dynamic/2267573?key=你的密钥
后面的路由路径完全不变,这是 RSSHub 的设计 —— 路由是通用的,换的只是实例地址。
路由怎么找?官方文档 docs.rsshub.app 里有全量列表,按站点分类。你也可以直接访问 https://rss.example.com/api/namespace?key=你的密钥 看自己的实例支持哪些路由。
顺手配个监控。 RSSHub 的路由会因为目标站改版而失效,你可能订阅好几天没更新才发现。用 Uptime Kuma 监控几个关键路由的 URL,返回内容异常或者状态码不对就告警,比事后发现靠谱。
RSSHub 的路由修复很频繁 —— 某平台改版,社区通常几天内就会提交修复。所以保持更新是必要的。
cd ~/rsshub
docker compose pull
docker compose up -d
镜像 tag 是日期格式(比如 2026-10-01),想稳一点可以锁版本,看到更新日志说修好了你在用的路由再升。
更新前留意 breaking change。 RSSHub 偶尔会调整路由路径或者废弃某些路由,更新后如果发现订阅断了,先去看一眼仓库的 release notes。
访问路由一直转圈然后 503:多半是浏览器路由 + 内存不够。先看 docker stats 里 browserless 容器的内存占用,然后调低 MAX_CONCURRENT_SESSIONS。
返回的内容是空的:目标站改版了,等社区修,或者去 GitHub 提 issue。也可以自己写路由(RSSHub 的贡献流程不复杂,但那是另一个话题了)。
B 站路由报 401:Cookie 没配或者过期了。重新抓一次。
内存一直涨最后 OOM:Redis 和浏览器进程都会累积。给容器设置内存上限让 Docker 自动重启,或者定期 docker compose restart。
磁盘被镜像占满:RSSHub 的镜像更新很勤,老镜像不会自动删。docker image prune -a 清理一下,但注意别把正在用的删了。
订阅源在国内加载很慢:你的服务器在海外的话,抓国外的源快,抓国内的源反而慢。可以考虑国内源走公共实例、国外源走自己的实例 —— 这也是很多人的做法。
RSSHub 的价值在于把信息获取的主动权拿回来 —— 不用再打开十个 App 刷更新,所有内容汇到一个阅读器里按时间线排好。
它不算难跑,真正的门槛是两个:内存要够(浏览器路由),Cookie 要维护(国内平台)。想清楚这两点再决定要不要自建,如果只是偶尔订阅几个博客,公共实例其实够用。
延伸阅读:
