打造一条干净的 RSS 订阅流:RSSHub + 透明代理图床 + Reeder 体验优化,彻底搞定 RSSHub 防盗链与图片超时

自建 RSSHub 最膈应的痛点莫过于文章刷出来了,图片却遇到微信、知乎等平台的防盗链拦截,或是 Telegram 等海外 CDN 直连报错 504 Timeout。为了让图床稳定加载,同时不污染 NAS 宿主机的网络环境、不影响内网穿透与 PT 下载,本文记录了一套基于 Nginx (imgproxy) 伪装 Referer + tun2socks 伴生透明网关 (Sidecar) 的图床架构。不改动主路由、不开主机的全局代理,让图床容器独享 SOCKS5 转发链路,搭配最新订阅制 Reeder 客户端,实现全量 RSS 图文零裂图、内网级秒开的阅读体验。
由于现在的互联网环境高度封闭,用自建 RSSHub 抓取回来的正文虽然整洁,但图片通常会面临三重拦路虎:
- 防盗链拦截:微信公众号(
qpic.cn)、知乎(zhimg.com)、微博(sinaimg.cn)、少数派(sspai.com)等平台,一旦检测到 HTTP 请求头里的Referer不来自自家主站,直接返回 403 或占位坏图。 - 海外被封阻图床:Telegram 频道(
telesco.pe/t.me)、X/Twitter 等平台的 CDN,在国内直连直接报错超时。 - 连接池与请求速度:每次在客户端打开一篇文章,几十张图片并发发往不同的源站,极易触发反爬限制,且移动端蜂窝网络下的加载体验极差。
为了解决这三个问题,并在买断版本 Reeder 5中获得秒开无坏图的终极体验,一整套实践架构的思路由三部分串联而成:RSSHub 生产源内容 → Nginx (imgproxy) 解决防盗链与磁盘缓存 → tun2socks (Sidecar 网关) 打通海外封锁 → Reeder 稳定消费。
整体网络架构与链路隔离
在 NAS 上搞容器代理,最忌讳就是直接把主机网络改得面目全非。QNAP NAS 上往往运行着 PT/BT 下载、cloudflared tunnel 内网穿透、本地虚拟交换机以及系统内置服务。如果直接在宿主机层面开启 Mihomo/Clash 的全局 TUN 增强模式,会出现明显的破坏性副作用:
cloudflared的 UDP/QUIC 隧道会被强制截取或丢包,导致连接持续中断或断崖式降级。- PT 下载可能会误走海外节点,一方面耗尽机场流量,另一方面立马被机场规则封号。
所以,这套配置坚持使用 Sidecar(伴生容器)透明网络隔离:让 Nginx 图床服务直接寄生在一个极轻量的 tun2socks 容器的网络空间下。只有它自己抓图时才走代理,全机其余网络干净如初。
+-------------------------------------------------------------------------------+
| QNAP NAS (Host 宿主机) |
| 代理监听端口:Mihomo/Clash -> 172.29.8.1:7891 (SOCKS5/HTTP 混合) |
| |
| +-------------------------------------------------------------------------+ |
| | Container Station (Docker Compose) | |
| | | |
| | +-------------------------------------------------------------------+ | |
| | | tun2socks-gateway (网关容器, 对外映射 8888:80) | | |
| | | [虚拟网卡 tun0] <-- SOCKS5 转发 --> 172.29.8.1:7891 | | |
| | | ^ | | |
| | | | (共享同一个命名空间与虚拟网卡 network_mode: service:...) | | |
| | | imgproxy (Nginx 缓存与防盗链伪装) | | |
| | +-------------------------------------------------------------------+ | |
| | | |
| | +-------------------------------------------------------------------+ | |
| | | rsshub (独立的 RSS 源生产容器, 输出正文与图片链接) | | |
| | +-------------------------------------------------------------------+ | |
| +-------------------------------------------------------------------------+ |
+-------------------------------------------------------------------------------+
^
| HTTP/HTTPS 请求
+-------------------------------------------------------------------------------+
| 终端客户端:Reeder 5 |
| 读取 RSSHub 源 -> 遇到图片标签访问 http://NAS_IP:8888/imgproxy?url=... |
+-------------------------------------------------------------------------------+
第一模块:RSSHub 本地自建配置
RSSHub 作为最上流的源生产单元,在容器编排中保持独立即可,无需接入 SOCKS5 代理网关(如果有需要抓取被墙的 RSS 源,可通过 RSSHub 自身的 HTTP 代理环境变量单发代理)。
docker-compose.yml(RSSHub 单独服务段)
version: '3.8'
services:
rsshub:
image: diygod/rsshub:latest
container_name: rsshub
restart: always
environment:
- NODE_ENV=production
- CACHE_TYPE=memory
- CACHE_EXPIRE=3600
- PUPPETEER_WS_ENDPOINT=
# 如果想让 RSSHub 也能正常解析外网源,配置单独的代理即可:
- PROXY_URI=http://172.29.8.1:7891
ports:
- "1200:1200"
第二模块:Nginx 缓存图床(imgproxy)的核心防盗链规则
这是整套系统处理图片最基础的第一道关:解决防盗链。
大部分基于 Referer 的反防盗链逻辑都很简陋:服务器判断你请求图片时的 Referer 头是否符合自己主站域名。不符合就拒绝。用 Nginx 作为前置反向代理时,我们可以利用 map 功能,精准匹配 URL 中的目标主站,强制伪装对应的 Referer。
Nginx 防盗链伪装匹配字典
| 目标 CDN 域名特征 | 命中平台 | Nginx 强行注入的 Referer |
|---|---|---|
~*sspai\.com | 少数派 | [https://sspai.com/](https://sspai.com/) |
~*zhimg\.com | 知乎 | [https://www.zhihu.com/](https://www.zhihu.com/) |
~*yuque\.com | 语雀 | [https://www.yuque.com/](https://www.yuque.com/) |
~*doubanio\.com | 豆瓣图片 | [https://www.douban.com/](https://www.douban.com/) |
~*douban\.com | 豆瓣 | [https://www.douban.com/](https://www.douban.com/) |
~*chongdiantou\.com | 充电头网 | [https://www.chongdiantou.com/](https://www.chongdiantou.com/) |
~*qpic\.cn | 微信公众号 | [https://mp.weixin.qq.com/](https://mp.weixin.qq.com/) |
~*sinaimg\.cn | 新浪微博 | [https://weibo.com/](https://weibo.com/) |
nginx.conf 完整生产级配置
将以下内容保存为 /share/Container/imgproxy/nginx.conf:
events {
worker_connections 1024;
}
http {
# 使用国内外通用公共 DNS,配合 SOCKS5 代理确保抗污染和干净解析
resolver 223.5.5.5 119.29.29.29 8.8.8.8 valid=300s ipv6=off;
resolver_timeout 5s;
# 磁盘缓存设置:最多占用 1G,最长保存 7 天
proxy_cache_path /tmp/nginx_cache levels=1:2 keys_zone=img_cache:100m inactive=7d max_size=1g;
map $arg_url $matched_referer {
default "";
"~*sspai\.com" "https://sspai.com/";
"~*zhimg\.com" "https://www.zhihu.com/";
"~*yuque\.com" "https://www.yuque.com/";
"~*doubanio\.com" "https://www.douban.com/";
"~*douban\.com" "https://www.douban.com/";
"~*chongdiantou\.com" "https://www.chongdiantou.com/";
"~*qpic\.cn" "https://mp.weixin.qq.com/";
"~*sinaimg\.cn" "https://weibo.com/";
}
server {
listen 80;
server_name _;
location /imgproxy {
if ($arg_url !~* "^https?://") {
return 400 "Invalid URL";
}
proxy_cache img_cache;
proxy_cache_valid 200 7d;
# 当源站网络抖动或超时时,允许使用陈旧的本地过期缓存先给阅读器展示
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_pass $arg_url;
proxy_set_header Referer $matched_referer;
proxy_set_header User-Agent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
proxy_set_header Accept "image/avif,image/webp,image/apng,image/*,*/*;q=0.8";
proxy_ssl_server_name on;
proxy_ssl_protocols TLSv1.2 TLSv1.3;
proxy_hide_header Access-Control-Allow-Origin;
add_header Access-Control-Allow-Origin *;
proxy_hide_header Cache-Control;
add_header Cache-Control "public, max-age=604800";
}
}
}
第三模块:Sidecar 伴生透明网关(tun2socks)的进阶融合
常规图床可以用上面的 Nginx 解决,但如果抓取 Telegram(如 cdn1.telesco.pe)等被墙图片,Nginx 会抛出 504 Gateway Timeout。如果简单粗暴地在配置里写 HTTP 代理转发,又会遇到各类协议不适配现象。
核心组网配置(图床代理模块 docker-compose.yml)
version: '3.8'
services:
# 1. 透明代理网关容器:创建虚拟网卡,接管同网络栈下所有的 TCP/UDP 流量
tun2socks-gateway:
image: xjasonlyu/tun2socks:v2.5.2
container_name: tun2socks-gateway
restart: always
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
environment:
# 必须指向 NAS 上支持 SOCKS5 协议的监听端口 (默认 mixed 端口可写 7890/7891)
- PROXY=socks5://172.29.8.1:7891
ports:
# Nginx 对外提供服务的 8888 端口在这里声明
- "8888:80"
# 2. 寄生于网关的 Nginx 中转图床容器
imgproxy:
image: nginx:alpine
container_name: imgproxy
restart: always
# 核心:复用伴生容器的网络栈,自动具备透明抓墙能力
network_mode: "service:tun2socks-gateway"
depends_on:
- tun2socks-gateway
volumes:
- /share/Container/imgproxy/nginx.conf:/etc/nginx/nginx.conf:ro
- /share/Container/imgproxy/cache:/tmp/nginx_cache
关键网络参数对比解析
| 配置/做法 | 具体配置代码 | 底层原理与为什么要这么配 |
|---|---|---|
| 错误代理方式 | PROXY=[http://172.29.8.1:7891](http://172.29.8.1:7891) | HTTP 代理无法转发 UDP 报文。Nginx 向 DNS 服务器发出的域名解析请求是 UDP 53 端口,会被直接丢弃,在日志报错 could not be resolved (110: Operation timed out) 继而导致 502 Bad Gateway。 |
| 正确代理方式 | PROXY=socks5://172.29.8.1:7891 | SOCKS5 天然兼容 UDP Associate 转发。可以完整承载 Nginx 对外的 UDP 域名查询,避开代理通道丢包的问题。 |
| 正确容器网关 | network_mode: "service:tun2socks-gateway" | Docker 原生特性。让 imgproxy 容器没有单独的以太网卡,直接用 tun2socks 初始化好的虚拟网卡 tun0。出站流量无感知进入 SOCKS5 代理。 |
折腾过程中的核心报错排查指南
| Nginx 报错现象 | 根源排查说明 | 最佳解决手段 |
|---|---|---|
504 Gateway Timeout | 直连被墙 CDN(如 telesco.pe)长时间无响应 | 加上 tun2socks-gateway 容器,引入 Sidecar 转发逻辑。 |
502 Bad Gateway (且带有 110: Operation timed out) | tun2socks 中配置的 http:// 代理协议把 Nginx 的 UDP DNS 解析包给吞了 | 将网关环境变量修改为 socks5://172.29.8.1:7891。 |
502 Bad Gateway (且带有 2: Server failure) | 配置了 Docker 内部 DNS 127.0.0.11,但流量最终走了运营商有污染的 DNS | 在 nginx.conf 中填入纯净组合公网 DNS:223.5.5.5 119.29.29.29 8.8.8.8。 |
HTTP 200 OK (正常抓取,但 Reeder 里显示裂图) | 目标源为微信公众号或少数派等强防盗链系统,未正确返回伪装 Referer | 检查 map $arg_url $matched_referer 是否覆盖该主站,如果有漏,按正则规则补上一行即可。 |
第四模块:终端阅读器(Reeder)的联动配置
一切后置设施配置完成、执行 docker compose up -d 启动后,你的图片缓存转发接口已经确定为:
http://NAS_IP:18888/imgproxy?url=真实图片URL
在终端设备(iPhone / Mac)上,使用最新订阅制版本的 Reeder(Reeder 2024+ / Version 2024.9.1 及后续系列)。这个版本比起老版的 Reeder 5 在界面流式交互和多设备云同步上做了大幅重构,对于有高频刷图需求的订阅流特别有效。我还是习惯使用老版本的 Reeder 5。
在 RSS 源中应用 imgproxy 转换的两种策略
策略 A:直接在 RSSHub 生产源时完成替换(最推荐)
如果你的 RSS 抓取源是自建或自己能够修改模板的内容,建议在生成 RSS 报文时,直接将其中的 <img src="https://..."> 全部利用脚本或中间件重定向:
原始:<img src="https://cdn1.telesco.pe/file/xxx.jpg">
替换后:<img src="http://NAS_IP:8888/imgproxy?url=https://cdn1.telesco.pe/file/xxx.jpg">
此时你在 Reeder 中订阅这个改过 URL 的 RSS 地址即可,没有任何额外适配。
策略 B:用 Nginx 前置给 RSSHub 做响应体改写(进阶方案)
可以通过 Nginx 的 sub_filter 模块,再开一个反向代理端口套在 RSSHub 前面。每当 Reeder 去请求 RSS 列表时,Nginx 自动把文本里的 https:// 匹配并前缀补上你的 http://NAS_IP:8888/imgproxy?url=。这能彻底免去修改源脚本的成本。
买断版 Reeder 5 的实际体验提升
接入这一套自建缓存链条之后,在 Reeder 客户端里能直观地看到体验跃迁:
- 微信 / 知乎 / 微博长文秒开:通过伪装 Referer 和服务器本地千兆网络代理,客户端再无空白图和破损图。
- Telegram 图文推送即刷即看:即便手机处于直连或非分流网络,依然可以直接借由 NAS 宿主机的透明网关顺利将图片抓回。
- 极速本地预载:由于 Nginx 配置了
/tmp/nginx_cache磁盘快取,第一次某设备加载完成后,本地其他设备的 Reeder 再次刷到这篇内容,完全由 NAS 内网直接吐出,省去了源站二次解析以及带宽抖动等待。
总结与检查清单
整个自建图床系统的优化核心在于:让专业的工具在隔离的空间做专业的事。
- RSSHub 只管去各大目标站点摘取结构化内容,不做无意义的数据脏转换;
- Nginx (
imgproxy) 只专注搞定 HTTP 头部重写、协议校验与磁盘缓存,不需要关心理底层的网络隧道如何建立; - tun2socks 作为最小化权限的 Sidecar,只收容这一个 Nginx 图床容器发出的流量,把最困难的国内外网络路由静默卸载到主机的代理端口。
如果你需要对新的服务快速建立同款无缝抓图代理,不用改写主机的路由表,只需新建一个需要翻墙的容器,在其 Docker Compose 里挂载一条 network_mode: "service:tun2socks-gateway" 即可直接把链路接通。

现在我的 Reeder 5 大部分内容都是有图的包括豆瓣,体验好很多。
flowchart TD
subgraph Client ["终端设备 (iPhone / Mac)"]
R["Reeder 5"]
end
subgraph NAS_Docker ["QNAP NAS : Container Station (Docker Bridge)"]
subgraph Sidecar_Pod ["共享网络空间 (network_mode: service:tun2socks-gateway)"]
GW["tun2socks-gateway<br/>监听: 8888:80<br/>虚拟网卡: tun0"]
NG["imgproxy (Nginx)<br/>防盗链 Referer + 7天磁盘缓存"]
end
RH["rsshub<br/>监听: 1200<br/>生成带 /imgproxy 前缀的图片 URL"]
end
subgraph NAS_Host ["QNAP NAS : 宿主机主机网络"]
MI["Mihomo / Clash 代理服务<br/>监听: 172.29.8.1:7891 (SOCKS5/HTTP)"]
end
subgraph CDN ["目标图片源站"]
CN["国内图床 (微信/知乎/少数派)<br/>校验 Referer 正常放行"]
TG["海外被墙图床 (Telegram cdn1.telesco.pe)<br/>通过代理出境"]
end
%% 数据流向
R -- "1. 订阅文本请求" --> RH
R -- "2. 图片请求 http://NAS_IP:8888/imgproxy?url=" --> GW
GW <-->|网络命名空间共享| NG
NG -- "3. 命中缓存直接高速返回" --> R
NG -- "4. 未命中缓存 (TCP抓取 / UDP 53 DNS)" --> GW
GW -- "5. SOCKS5 代理穿透" --> MI
MI -- "国内规则" --> CN
MI -- "代理规则" --> TG