未坐标系

FreshRSS 自托管订阅服务&快速订阅插件

本文将详细介绍 FreshRSS 的自托管完整流程,包括使用 Docker 部署、配置 Nginx 反向代理、进行验证和故障排除,并提供一个油猴脚本以便快速订阅您喜爱的站点。

2026/07/06
loading

FreshRSS 自托管订阅服务&快速订阅插件

本文将详细介绍 FreshRSS 的自托管完整流程,包括使用 Docker 部署、配置 Nginx 反向代理、进行验证和故障排除,并提供一个油猴脚本以便快速订阅您喜爱的站点。


一、为什么自托管 RSS

RSS 阅读器选择不少,我最后选了 FreshRSS,理由很实际:

  • 部署成本低:Docker Compose 一把起,不依赖重基础组件
  • 成熟度够:不是那种只能演示、不能长期养的项目
  • 多用户支持:自用够了,给朋友开账号也不需要重构
  • 扩展性好:原生 RSS/Atom 之外的来源,HTML+XPath 兜底

主要是名气大,别的也没用过 先看一下效果

Pasted image 20260706224410.png

二、部署目标

我的环境是这样:

  • Linux VPS,Docker Compose 部署
  • 应用只监听本机回环,不直接暴露公网
  • 独立域名,Nginx 反代入口
  • 先用 FreshRSS 自带的 SQLite,不急着上 PostgreSQL

应用只跑回环,公网统一收口到 Nginx。这样做的好处:HTTPS、访问控制、日志全在反代层处理,迁移和排障时层次分明。


三、部署:从 docker-compose.yml 说起

3.1 目录准备

/opt/freshrss
├── docker-compose.yml
├── data/
└── extensions/
mkdir -p /opt/freshrss/{data,extensions}
cd /opt/freshrss

3.2 Compose

services:
  freshrss:
    image: freshrss/freshrss:latest
    container_name: freshrss
    restart: unless-stopped

    ports:
      - "127.0.0.1:3010:80"             # 【关键】只绑回环,外部无法直连,必须经 Nginx

    volumes:
      - ./data:/var/www/FreshRSS/data               # 用户数据、配置、SQLite 数据库
      - ./extensions:/var/www/FreshRSS/extensions   # 第三方扩展单独挂,不和数据混

    environment:
      TZ: Asia/Shanghai               # 时区,影响 cron 执行时间
      CRON_MIN: "3,33"               # 每小时的第 3、33 分钟抓取一次,自定义

volumes 为什么拆成两个?

如果只挂一个 /var/www/FreshRSS 整目录,扩展和运行数据搅在一起。拆开后:

  • data/ 存订阅数据、用户配置、数据库文件,容器重建不会丢
  • extensions/ 装第三方扩展时直接往宿主机目录放文件,容器内自动可见,不用每次 docker cp

CRON_MIN 的设置逻辑

FreshRSS 用内置 cron(不是宿主机 crontab)定时抓取订阅源。CRON_MIN: "3,33" 表示每小时的第 3 和第 33 分钟各跑一次,一天 48 次。个人使用够了,也不算激进。订阅源多或服务器带宽有限的话,改成 "8,38" 甚至一天只跑几次也可以。

为什么不用外部数据库?

FreshRSS 默认用 SQLite,数据就在 data/ 目录里一个文件,零维护成本。PostgreSQL 当然可以用,官方镜像支持 DB_HOST 等环境变量,但个人 RSS 阅读器的并发和数据量远到不了 SQLite 的瓶颈。先把服务跑稳,真遇到性能问题再迁也不迟。

3.3 启动

docker compose up -d
docker logs --tail=100 freshrss   # 看启动日志,确认无报错
curl -I http://127.0.0.1:3010    # 本机验证,不通就先别往下走

3.4 Nginx 反代

给 FreshRSS 挂独立域名(如 rss.example.com),写一个反代配置。proxy_pass 指向刚才的回环地址,带上必要的转发头:

server {
    listen 80;
    server_name rss.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name rss.example.com;

    ssl_certificate     /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3010;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
nginx -t && systemctl reload nginx

四、FreshRSS Helper:让没有 RSS 的网站也能订阅

服务搭好了,但很多网站压根不提供 RSS。要么作者没配,要么 RSS 藏在某个 /feed/rss.xml/atom.xml 路径里你找不到。

FreshRSS 自身有 HTML+XPath 抓取模式,能解析任意网页结构。但要在添加订阅页手填七八个 XPath 字段,门槛不低,得会写 XPath,得去源码里翻节点,得反复试错。

手动F12试了下抓取XPath路径,我选择放弃 Codex启动!写了一个油猴脚本,解决了以下痛点

痛点脚本方案
不知道 RSS 地址在哪自动探测 /feed/rss.xml 等常见路径,按靠谱程度排序
没有 RSS,要写 XPath可视化 XPath 拾取器,鼠标点选填好所有字段
XPath 字段多手填烦跳到 FreshRSS 添加页时自动按语义匹配填充

工作流程两步:

  1. 源页面:点按钮,探测 RSS 或拾取 XPath,结果暂存到浏览器
  2. FreshRSS 添加页:脚本读取暂存数据,自动填进输入框

两个模式靠 URL 自动切换,不用手动干预。

安装

先装脚本管理器(二选一):

管理器特点
Tampermonkey主流选择,全浏览器支持
Violentmonkey开源轻量

然后在管理器仪表盘 → 添加新脚本 → 导入解压缩后的 freshrss-helper-lite.user.js → 保存。

点击展开下载脚本

https://greasyfork.org/zh-CN/scripts/585899-freshrss-helper

首次配置

  1. 打开任意网页,右下角会出现土黄色「配置 FreshRSS」按钮
  2. 点击,输入你的 FreshRSS 根地址(如 https://rss.example.com),只填根地址不带后缀
  3. 保存后按钮变为「RSS通用标志」,以后点它就进入订阅流程

按住按钮可拖动位置
Shift+左键点击:可重新配置FreshRSS 基址
右键点击:直接排除当前网站及其子域名显示订阅按钮
Ctrl+右键点击:管理排除域名列表


五、实战场景

点按钮后脚本的决策逻辑:先判断是不是 GitHub 仓库,再探测 RSS/Atom,都没有就切 HTML+XPath 模式。下面挑几个有代表性的场景走一遍。

场景 1:GitHub 仓库监控

GitHub 自带 Atom 源,这个场景最省事。

打开任意仓库页,点按钮,脚本弹出四个选项:

Pasted image 20260706221551.png

选项源路径用途
订阅 Releases/releases.atom版本发布(最常用)
订阅 Issues/issues.atom新 Issue / 更新
订阅 Pull Requests/pulls.atomPR 动态
订阅 Commits/commits.atom主分支提交(高频,慎用)

按需选择后点击相应订阅按钮,自动跳转到FreshRSS添加订阅页面,手动点填充按钮,提交

Pasted image 20260706222458.png

场景 2:有 RSS 的个人博客

典型:大部分博客都有RSS,但没有放置RSS图标。

  1. 打开博客首页(不是单篇文章页)
  2. 点按钮,脚本探测 RSS 路径
  3. 按推荐路径点击订阅
  4. 点「添加到 FreshRSS」,新标签打开添加页,点填充图标,提交

Pasted image 20260706222859.png

场景 3:无 RSS 的个人博客

典型:某独立开发者的 Hexo / Hugo 博客,作者没配 RSS。

  1. 打开博客首页(不是单篇文章页)
  2. 点按钮,脚本探测 RSS 路径大概率找不到,自动切到 XPath 拾取模式
  3. 按顺序拾取(鼠标移到元素上会出现高亮框,点击选中):
字段拾取目标
文章容器(必填)每篇文章的根节点,通常是 <article> 或带 class="post"<div>
文章标题(必填)标题文字,通常是 <h2> / <h3>
文章内容(必填)摘要或正文片段
文章链接标题外层 <a>,脚本通常自动找到
文章日期<time> 标签
文章作者 / 缩略图选填,可点「自动」按钮推断
  1. 填完必填三项后点「测试标题/内容」,脚本跑一次 XPath,返回匹配数和命中率
  2. 验证通过,点「添加到 FreshRSS」,新标签打开添加页,点「手动填充」,提交

Pasted image 20260706223425.png
Pasted image 20260706223520.png
Pasted image 20260706223605.png
Pasted image 20260706223924.png


六、XPath 拾取技巧

脚本的设计目标是让你不用学 XPath,所有场景靠鼠标点击完成。但了解几个概念能处理疑难情况。

脚本生成的 XPath 风格

优先使用语义化 class,内部字段用 descendant:: 相对定位:

//article[contains(@class, "post")]           ← 文章容器
descendant::h2[contains(@class, "title")]      ← 标题
descendant::a/@href                             ← 链接
descendant::time/@datetime                      ← 日期
descendant::img/@src                            ← 图片

测试功能

跳到 FreshRSS 之前,点「测试标题/内容」。它会告诉你容器匹配了多少个、标题/内容命中率多少。这是最直接的反馈,比来回切到 FreshRSS 试快得多。

常见测试失败

现象原因解决
容器匹配 0XPath 写错 / 页面动态渲染等加载完,重新拾取
容器匹配过多XPath 太宽泛(选了通用 <div>选有具体 class 的容器重新拾取
标题命中 0/N标题节点不在预期位置检查是否被 <header> / <a> 包裹,重新拾取
标题命中部分部分文章结构不同(如置顶帖)通常可接受,看实际抓取效果决定

七、调试与排错

字段识别报错:FreshRSS 添加页出现 XPath 语法错误,可能是脚本误把纯文本填进了 XPath 字段。检查脚本是否最新版,看 toast 提示的「已跳过(非 XPath)」字段,或重置 freshrss_pending_payload 重新拾取。

Cloudflare 拦截:源页面如果是安全检查页,脚本会主动提醒。FreshRSS 服务器 IP 可能被屏蔽,需要配代理或找替代源。

字段没自动填:字段标签在不同 FreshRSS 版本/语言下可能不一样。手动填那一个字段即可,或报 issue 给脚本作者。

按钮消失:可能是页面 CSP 严格阻止了样式注入、用了 iframe、或加载时机问题。刷新页面等几秒再看。


八、隐私与存储

  • 脚本不向任何第三方发送数据
  • 只在两种页面运行:你点按钮时的源页面、你的 FreshRSS 添加页
  • 探测 RSS 的请求从你浏览器发出,目标网站看到的是你的 IP,和正常访问没区别
  • FreshRSS 地址只存在本地浏览器
  • 跨标签页数据通过 GM 存储 + localStorage 双写传递
  • 重置配置:在 Tampermonkey 里清空脚本存储,或用浏览器开发者工具删除相关 key

参考链接

CATALOG