惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tailwind CSS Blog
C
CERT Recently Published Vulnerability Notes
P
Proofpoint News Feed
Vercel News
Vercel News
博客园 - 三生石上(FineUI控件)
IT之家
IT之家
Help Net Security
Help Net Security
月光博客
月光博客
N
News and Events Feed by Topic
Cloudbric
Cloudbric
博客园 - 司徒正美
L
LangChain Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
Tenable Blog
The Register - Security
The Register - Security
The Hacker News
The Hacker News
I
InfoQ
The Last Watchdog
The Last Watchdog
MyScale Blog
MyScale Blog
Schneier on Security
Schneier on Security
WordPress大学
WordPress大学
小众软件
小众软件
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
宝玉的分享
宝玉的分享
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
K
Kaspersky official blog
L
LINUX DO - 热门话题
N
News | PayPal Newsroom
F
Fortinet All Blogs
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
S
Security @ Cisco Blogs
Recorded Future
Recorded Future
大猫的无限游戏
大猫的无限游戏
H
Help Net Security
Google Online Security Blog
Google Online Security Blog
S
Schneier on Security
C
Cisco Blogs
N
News and Events Feed by Topic
V2EX - 技术
V2EX - 技术
Latest news
Latest news
PCI Perspectives
PCI Perspectives
T
The Blog of Author Tim Ferriss
P
Palo Alto Networks Blog
T
Tor Project blog
Project Zero
Project Zero
云风的 BLOG
云风的 BLOG
Webroot Blog
Webroot Blog
Attack and Defense Labs
Attack and Defense Labs
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org

沉迷在 - Java.li - 无法自拔

Spring Boot启动报错 Failed to configure a DataSource 解决办法 Maven依赖冲突怎么排查?dependency:tree、exclusions与版本统一完整教程 - 沉迷在 - Java.li - 无法自拔 给网站加一条会动的人群横幅:Little People 动画组件 Demo - 沉迷在 - Java.li - 无法自拔 Spring Boot 2升级到Spring Boot 3完整指南:JDK17、javax迁移jakarta、依赖兼容与常见报错 - 沉迷在 - Java.li - 无法自拔 随机小姐姐视频 - 沉迷在 - Java.li - 无法自拔 Spring Boot与JDK版本兼容表:Spring Boot 2.x / 3.x / 4.x应该用哪个Java版本? - 沉迷在 - Java.li - 无法自拔 No compiler is provided in this environment解决办法:JDK、JRE、Maven与IDEA排查 - 沉迷在 - Java.li - 无法自拔 置身钉内|含全文 PDF - 沉迷在 - Java.li - 无法自拔 javac不是内部或外部命令怎么解决?JDK、JAVA_HOME和Path完整排查 Windows配置JAVA_HOME后不生效怎么办?java -version显示旧版本解决办法 Maven Could not transfer artifact 下载失败解决:settings.xml、国内镜像、本地缓存与代理排查 JSON转Java实体类完整教程:对象、数组、嵌套结构与LocalDateTime处理 Windows安装JDK 8 / 17 / 21 / 25完整教程:JAVA_HOME环境变量配置与验证 IDEA下载JDK很慢怎么办?手动配置本地JDK完整教程 Java版本号与class文件major version对照表:Unsupported class file major version 52 / 55 / 61 / 65 / 69 / 70 Gradle国内镜像配置教程:init.gradle、repositories与Wrapper加速完整指南 Maven国内镜像settings.xml配置大全:阿里云、腾讯云、华为云、清华源 Java开发者必备工具箱 2026年Java行情深度解读:就业真实现状、语言排名与开发者破局指南 如何防止服务器被暴力破解?2026年最全的5层防护实战指南 Go-RSS-Hub:我用 Go 搭了一个可扩展的 RSS 聚合服务 Vibe Coding 实战复盘 2026年全球十大顶级大语言模型横向对比报告 万能插座与十八般兵器:MCP 和 Skill 解决MySQL ONLY_FULL_GROUP_BY 模式导致的 SQL 语法错误 记一次处理服务器被暴力破解(含黑名单IP) Vibe Coding:一名“古法程序员”的使用反思与未来思考 .li后缀续费的特殊性(某些顶级域名在到期前可能被提前删除) 浅谈域名后缀以及市场前景 咖啡与编程 超越基础的编程智慧
【soso.re】聚合热榜 | CloudflareWorkers部分接口失效的排查与代理方案 - 沉迷在 - Java.li - 无法自拔
HiF · 2026-07-07 · via 沉迷在 - Java.li - 无法自拔

这篇文章记录一次真实的线上排查过程:一个 Next.js 热榜聚合项目,本地运行正常,部署到 Cloudflare Workers 后,哔哩哔哩、虎扑、快手、懂车帝等来源加载失败。

这次案例来自我自己维护的热榜聚合站:soso.re。它聚合了微博、知乎、哔哩哔哩、虎扑、快手、V2EX 等多个来源,线上环境部署在 Cloudflare Workers 上。如果你想先看最终效果,可以直接打开 https://soso.re 体验。

Snipaste_2026-07-07_09-31-03-kpnxvahb.png

最终采用的方案是:

  • Cloudflare Workers 继续负责前端和大部分 API。
  • 对容易被限制的热榜源,走一台自己的轻量服务器做固定路径代理。
  • 对已经需要登录验证、页面结构不稳定的源,不继续硬绕,直接换成更稳定的公开源。

本文的配置示例以域名 example.com 代替真实域名,密钥也全部用占位符,请不要直接复制弱密钥到生产环境。

问题现象

项目部署到 Cloudflare Workers 后,部分 API 在线上返回空数据或错误,但本地开发环境正常。

典型表现:

/api/bilibili   失败
/api/hupu       失败
/api/kuaishou   失败
/api/dongchedi  解析为空或需要登录验证

本地请求正常,说明代码解析逻辑未必有问题;线上失败,更多是运行环境、出口 IP、请求头、平台反爬策略导致。

第一步:先加调试日志,不要盲改

最开始不要直接换接口或乱加请求头,先让 Worker 把上游响应状态、内容类型和响应体开头打印出来。

示例调试函数:

const HOT_SOURCE_DEBUG_PREFIX = '[hot-source-debug]';
const BODY_PREVIEW_LENGTH = 300;

export async function readHotSourceText(
  source: string,
  upstreamUrl: string,
  response: Response,
) {
  const body = await response.text();

  console.error(HOT_SOURCE_DEBUG_PREFIX, {
    source,
    upstreamUrl,
    status: response.status,
    ok: response.ok,
    contentType: response.headers.get('content-type'),
    bodyLength: body.length,
    bodyStart: body.slice(0, BODY_PREVIEW_LENGTH),
  });

  return body;
}

然后在 Cloudflare Dashboard 里打开:

Workers 和 Pages -> 你的 Worker -> Observability -> Workers Logs

可以按 [hot-source-debug] 搜索日志。

实际排查结果

线上日志里看到的情况大概是这样:

来源Worker 直连结果说明
哔哩哔哩412 text/html上游拒绝了 Cloudflare Workers 的请求特征
虎扑405 text/html页面请求在 Workers 环境下被拒绝
快手525 或页面结构缺失TLS/反爬/页面结构都可能影响
懂车帝200 text/html 但解析为空页面进入登录或验证态,原解析字段不存在

结论:这不是简单的网络波动,而是部分平台对 Cloudflare Workers 的出口、请求头或请求环境有限制。

方案选择

我最终没有选择把所有数据源都换成第三方聚合 API,因为这样会引入新的不确定性:第三方接口可能限流、变更、失效,甚至无法确认数据来源。

最终方案:

  1. 哔哩哔哩、虎扑、快手:走自己的服务器做固定路径代理。
  2. 懂车帝:由于页面需要验证登录,不继续绕,替换成 V2EX 官方热门主题接口。
  3. Worker 代码里保留原始上游地址作为 fallback,没有代理变量时仍可本地调试。

整体架构

soso.re 为例,用户访问的是 Cloudflare Workers 上的站点;大部分热榜源由 Worker 直接请求,少数容易被限制的来源再转到自己的服务器代理。

浏览器
  |
  v
Cloudflare Workers / Pages
  |
  | 大部分热榜源直接 fetch
  |
  | 对特殊来源:
  v
自有服务器 Nginx / OpenResty
  |
  v
Bilibili / Hupu / Kuaishou 上游

代理服务器只开放固定路径,例如:

/hot/bilibili
/hot/hupu
/hot/kuaishou

不要做成通用代理,也不要把目标 URL 作为参数暴露出去。

Worker 侧代码

项目里增加一个代理选择函数:

type ProxySource = 'bilibili' | 'hupu' | 'kuaishou';

const proxyPaths: Record<ProxySource, string> = {
  bilibili: '/hot/bilibili',
  hupu: '/hot/hupu',
  kuaishou: '/hot/kuaishou',
};

export function getHotProxyRequest(
  source: ProxySource,
  fallbackUrl: string,
  init?: RequestInit,
): { url: string; init?: RequestInit; proxied: boolean } {
  const proxyBase = process.env.HOT_PROXY_BASE?.replace(/\/$/, '');
  const proxyToken = process.env.HOT_PROXY_TOKEN;

  if (!proxyBase || !proxyToken) {
    return { url: fallbackUrl, init, proxied: false };
  }

  const headers = new Headers(init?.headers);
  headers.set('x-proxy-token', proxyToken);

  return {
    url: `${proxyBase}${proxyPaths[source]}`,
    init: {
      ...init,
      headers,
    },
    proxied: true,
  };
}

业务接口里使用:

const upstreamUrl = 'https://api.bilibili.com/x/web-interface/ranking/v2?rid=0&type=all&callback=__jp0';
const request = getHotProxyRequest('bilibili', upstreamUrl, {
  headers: {
    Referer: 'https://www.bilibili.com/ranking/all',
    'User-Agent':
      'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36',
  },
});

const response = await fetch(request.url, request.init);
const text = await readHotSourceText('bilibili', request.url, response);

这样有两个好处:

  • 本地没有配置代理变量时,仍然走原始上游地址。
  • 线上配置了代理变量后,只影响指定数据源。

Cloudflare 变量配置

在 Cloudflare 后台添加运行时变量:

HOT_PROXY_BASE=https://example.com
HOT_PROXY_TOKEN=一段足够长的随机密钥

注意入口:

Workers 和 Pages -> 项目 -> 设置 -> 变量和密钥

这里要选 变量和密钥,不是 构建变量和密钥

原因是:

  • HOT_PROXY_BASEHOT_PROXY_TOKEN 是运行时请求 API 时使用的。
  • 构建变量只在 build 阶段可用,不能保证 Worker 运行时拿得到。

如果你使用 GitHub Actions、Wrangler 或 OpenNext 自动部署,还要注意:部署配置可能覆盖 Dashboard 里手动添加的变量。部署命令需要保留 Dashboard 变量,例如 OpenNext/Wrangler 场景中使用类似 --keep-vars 的参数。

Nginx / OpenResty 代理配置

下面配置放在原网站的同一个 server 块里,并且要放在普通 location / 之前。

先准备一个强随机密钥:

openssl rand -hex 32

假设得到:

REPLACE_WITH_LONG_RANDOM_TOKEN

哔哩哔哩代理

location = /hot/bilibili {
    if ($http_x_proxy_token != "REPLACE_WITH_LONG_RANDOM_TOKEN") {
        return 403;
    }

    proxy_pass_request_headers off;

    proxy_ssl_server_name on;
    proxy_ssl_name api.bilibili.com;

    proxy_set_header Host api.bilibili.com;
    proxy_set_header User-Agent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36";
    proxy_set_header Accept "application/json, text/plain, */*";
    proxy_set_header Accept-Language "zh-CN,zh;q=0.9,en;q=0.8";
    proxy_set_header Referer "https://www.bilibili.com/";
    proxy_set_header Connection "";

    proxy_pass https://api.bilibili.com/x/web-interface/ranking/v2?rid=0&type=all&callback=__jp0;
}

这里最关键的是:

proxy_pass_request_headers off;

它的作用是不要把 Cloudflare Worker 请求 example.com 时带来的原始请求头继续透传给 B 站。否则上游仍然能看到一些异常请求特征,可能继续返回 412

虎扑代理

location = /hot/hupu {
    if ($http_x_proxy_token != "REPLACE_WITH_LONG_RANDOM_TOKEN") {
        return 403;
    }

    proxy_pass_request_headers off;

    proxy_ssl_server_name on;
    proxy_ssl_name bbs.hupu.com;

    proxy_set_header Host bbs.hupu.com;
    proxy_set_header User-Agent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36";
    proxy_set_header Accept "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
    proxy_set_header Accept-Language "zh-CN,zh;q=0.9,en;q=0.8";
    proxy_set_header Referer "https://bbs.hupu.com/";
    proxy_set_header Connection "";

    proxy_pass https://bbs.hupu.com/all-gambia;
}

快手代理

location = /hot/kuaishou {
    if ($http_x_proxy_token != "REPLACE_WITH_LONG_RANDOM_TOKEN") {
        return 403;
    }

    proxy_pass_request_headers off;

    proxy_ssl_server_name on;
    proxy_ssl_name www.kuaishou.com;

    proxy_set_header Host www.kuaishou.com;
    proxy_set_header User-Agent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36";
    proxy_set_header Accept "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
    proxy_set_header Accept-Language "zh-CN,zh;q=0.9,en;q=0.8";
    proxy_set_header Referer "https://www.kuaishou.com/";
    proxy_set_header Connection "";

    proxy_pass https://www.kuaishou.com/?isHome=1;
}

原网站 location 不要被影响

如果这台服务器原本还跑着网站后台,例如 Halo、WordPress、管理面板等,原来的 location / 一定不要加 proxy_pass_request_headers off

推荐保持类似这样:

location / {
    proxy_pass_request_headers on;

    proxy_set_header Host $host;
    proxy_set_header Cookie $http_cookie;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;

    proxy_http_version 1.1;
    proxy_pass http://127.0.0.1:8090;
}

修改完成后检查并重载:

nginx -t
systemctl reload nginx

测试代理是否正常

不带密钥应该返回 403

curl -I https://example.com/hot/bilibili

带密钥应该返回 200

curl -I \
  -H "x-proxy-token: REPLACE_WITH_LONG_RANDOM_TOKEN" \
  https://example.com/hot/bilibili

再分别测试:

curl -I -H "x-proxy-token: REPLACE_WITH_LONG_RANDOM_TOKEN" https://example.com/hot/hupu
curl -I -H "x-proxy-token: REPLACE_WITH_LONG_RANDOM_TOKEN" https://example.com/hot/kuaishou

如果本机 curl 是 200,但 Worker 日志里仍然是上游错误,就继续看 Worker 调用代理后的日志。

Worker 日志如何判断问题

[hot-source-debug] 里的 upstreamUrl

如果还是原始地址:

https://api.bilibili.com/...

说明 Worker 没有读到代理变量,重点检查 Cloudflare 的运行时变量。

如果已经变成代理地址:

https://example.com/hot/bilibili

说明变量生效了,问题在代理服务器到上游这一段。

常见状态码判断:

状态可能原因处理方式
403代理密钥不匹配,或请求头没带上检查 HOT_PROXY_TOKEN 和 Nginx 里的密钥是否完全一致
404Nginx 没匹配到 /hot/* location检查配置是否放在正确的 server 块里
412上游识别到异常请求特征检查 /hot/* 是否设置了 proxy_pass_request_headers off 和浏览器请求头
405上游拒绝当前请求方式或环境使用固定路径代理,并伪装正常浏览器请求头
525SSL 握手或上游 TLS 问题检查 proxy_ssl_server_name onproxy_ssl_name
200 但解析为空页面结构变了,或进入登录/验证页面打印 bodyStart,必要时换源

懂车帝为什么换成 V2EX

懂车帝这类页面如果进入登录验证态,HTML 仍然可能返回 200,但页面里的业务数据已经没有了。

这时继续堆请求头、Cookie、解析规则,维护成本会很高,而且容易反复失效。

我的处理方式是换成 V2EX 热门主题接口:

https://www.v2ex.com/api/topics/hot.json

接口返回 JSON,结构清晰,不需要解析 HTML,比继续绕验证页稳定。

GitHub Actions 会不会影响仓库代码

单纯用 GitHub Actions 部署,不会自动修改仓库代码。

它通常只是:

拉取代码 -> 安装依赖 -> 构建 -> 部署到 Cloudflare

只有你的 workflow 里明确执行了 git commitgit push 这类命令,才会改仓库。

真正需要注意的是变量:

  • GitHub Actions 里的 Secrets 是给 CI 流程用的。
  • Cloudflare Dashboard 的 Variables and Secrets 是给 Worker 运行时用的。
  • 如果部署工具会同步 Wrangler 配置,可能覆盖 Dashboard 里手动设置的变量,所以要使用保留变量的部署参数,或把变量也纳入部署配置管理。

坑点总结

1. 本地正常不代表 Workers 正常

Cloudflare Workers 的出口、TLS、请求头和普通本机环境不一样。部分站点会对这类请求环境做限制。

2. 先看上游响应,不要只看前端失败

如果只看前端页面,会误以为是组件或缓存问题。实际要看 Worker 日志里的:

status
contentType
bodyStart
upstreamUrl

这个配置不能放在 server 级别,也不能放进原网站的 location /

否则会导致原网站登录后台拿不到 Cookie、CSRF 等请求信息,表现为:

点击登录后直接 Access Denied

4. /cdn-cgi/rum 返回 204 是正常的

这是 Cloudflare 的 Web Analytics / RUM 上报接口。

204 No Content 表示已接收但不返回内容,不是登录失败的原因。

5. Cloudflare Rocket Loader 可能影响后台登录页

如果你的网站后台登录页依赖前端 JS 加密密码或处理表单,Cloudflare 的 Rocket Loader、Auto Minify JS 等优化可能改写脚本执行时机。

遇到登录异常时,可以对后台路径关闭这些优化,例如:

/login*
/admin*
/console*

6. 代理密钥不要用弱口令

不要用 123456password 这类简单密钥。生成长随机值:

openssl rand -hex 32

如果密钥曾经在聊天记录、日志、截图或仓库里出现过,要立即更换。

7. 不要做开放代理

只开放固定路径:

/hot/bilibili
/hot/hupu
/hot/kuaishou

不要提供:

/proxy?url=https://...

开放代理很容易被滥用,也会给服务器带来安全和流量风险。

最终检查清单

上线前可以按这个顺序检查:

  • Nginx nginx -t 通过。
  • 不带 x-proxy-token 访问 /hot/* 返回 403
  • x-proxy-token 访问 /hot/* 返回 200
  • Cloudflare 运行时变量里配置了 HOT_PROXY_BASE
  • Cloudflare 运行时变量里配置了 HOT_PROXY_TOKEN
  • Worker 日志里的 upstreamUrl 已变成代理地址。
  • /api/bilibili/api/hupu/api/kuaishou 返回正常数据。
  • 原网站后台登录仍然正常。
  • 公开过的代理密钥已经轮换。

结论

这类问题的核心不是“Cloudflare Workers 不能用”,而是热榜项目依赖了大量第三方站点页面和接口。第三方站点对请求环境非常敏感,本地正常、线上失败很常见。

比较稳的处理方式是:

  • 能直连的源继续直连。
  • 容易被限制但仍然能稳定获取的源,走自有服务器固定路径代理。
  • 需要登录验证或结构频繁变化的源,及时换成更稳定的数据源。
  • 保留足够的调试日志,用状态码和响应体片段定位问题,而不是猜。

这样整体维护成本会低很多,也不会因为一个热榜源失效拖垮整个项目。

我已经把这套方案用在 soso.re 上。如果你也在做热榜聚合、导航页、信息流聚合这类项目,可以参考上面的代理方式;如果只是想直接看各平台热点,也欢迎把 soso.re 当作日常入口。