不是 Key 失效!我把 Hermes Agents 的 403 报错查了个底朝天,真凶居然是 Cloudflare

关键词:Hermes Agents、HTTP 403、Cloudflare 1010、OpenAI 兼容接口、User-Agent、自定义 Base URL、WAF 拦截

一、先说结论:这次 403,根本不是 API Key 失效

最近在排查 Hermes Agents 接自定义 OpenAI 兼容接口时报错的问题时,终端一直反复出现下面这类信息:

HTTP 403: Your request was blocked.
Non-retryable client error (HTTP 403). Aborting.
Your API key was rejected by the provider.

乍一看,很多人第一反应都是:

  • key 填错了
  • 账号没权限
  • 模型没开通
  • Hermes 配置有问题

但这次真正的根因,其实完全不是这些。

真实原因是:前置 Cloudflare / WAF 把默认 Python/OpenAI 客户端请求头拦截了。

也就是说:

  • 同一个 URL
  • 同一个 key
  • 同一个模型

换个 User-Agent,结果就完全不一样。

二、错误现象:程序提示“鉴权失败”,但其实是“请求头被拦”

出问题的场景是:

  • Hermes Agents 使用自定义 OpenAI-compatible endpoint
  • 例如:https://vip.auto-code.net
  • 发起 /v1/models 或实际模型请求时返回 403

最坑的是,这个 403 在原始逻辑里被进一步误判成了:

  • API key 被拒绝
  • 权限问题
  • auth failed

于是排查方向很容易被带偏,开始疯狂检查:

  • OPENAI_API_KEY
  • OPENAI_BASE_URL
  • 模型名是否正确
  • 配置文件是否写错

结果越查越偏,浪费大量时间。

三、我怎么确认这不是 Key 问题的?

这次定位问题的关键,不是盲猜,而是做了一个非常简单但极有效的对比实验:

1. 用默认 Python 请求头访问

返回结果:

HTTP 403
Error 1010: Access denied
Your request was blocked

2. 换成 CLI 风格的 User-Agent

例如:

User-Agent: Codex CLI

返回结果立刻变成:

HTTP 401 API_KEY_REQUIRED

看到这里,结论已经非常清楚了:

  • 403 不是模型服务本身拒绝了 key
  • 而是请求根本没进入正常鉴权层
  • 它先被 Cloudflare/WAF 在前面拦下来了

换句话说:

默认请求头被风控了,CLI 风格请求头能过。

四、真正的根因:Cloudflare 会拦一部分默认 Python / SDK 请求头

很多第三方 OpenAI 兼容接口,前面并不是裸服务,而是挂了:

  • Cloudflare
  • WAF
  • 防刷策略
  • 浏览器完整性检查

这类服务对下面这些请求特征非常敏感:

  • Python-urllib/*
  • 某些默认 OpenAI SDK 头
  • 看起来像脚本批量请求的客户端签名

所以你会看到一个非常迷惑的现象:

同一个 endpoint

默认请求头:

403 blocked

CLI 风格请求头:

401 缺 key / 200 正常响应

这就说明:

服务器不是“不认你的 key”,而是“压根不想让这类默认请求头进来”。

五、这次修复,不是只改一行配置,而是要改 4 个层面

很多人遇到这种问题,第一反应是:

那就在健康检查里加个 User-Agent 不就完了?

很遗憾,只改这一处,远远不够。

因为 Hermes Agents 里至少有两条链路:

  1. 探活链路

    • /v1/models
    • 健康检查
    • 模型可用性判断
  2. 真实调用链路

    • /responses
    • /chat/completions
    • 真正发模型请求

如果你只修探活:

  • 探活可能不报错了
  • 但真正对话时还是继续 403

所以这次修复的正确做法,是同时覆盖下面 4 个层面。

修复点 1:给 custom endpoint 的探活请求加 WAF-safe User-Agent

例如:

User-Agent: Codex CLI

这样 /v1/models 不会再因为默认 urllib 头被 Cloudflare 1010 拦掉。

修复点 2:给真正的 OpenAI 客户端请求也加同样的默认头

这一步特别重要。

因为很多修复方案只改了探活,却忘了主调用链。

结果就是:

  • 模型列表检查通过了
  • 真正请求模型时还是 403

所以必须让 custom provider 的真实请求客户端 也带上这个头。

修复点 3:把 403 blockedauth failed 区分开

不能再一看到 403 就粗暴归类成:

  • key 无效
  • 权限不足
  • auth failed

至少要把下面这些特征单独识别出来:

  • Cloudflare
  • 1010
  • Error 1010
  • Your request was blocked
  • Access denied
  • cf-mitigated

这些都应该走:

WAF / Cloudflare blocked

而不是:

Your API key was rejected by the provider

修复点 4:优化最终提示文案,别再误导排查方向

真正好的错误提示,不是把用户往错误方向带,而是要直接告诉他:

This 403 looks like a Cloudflare/WAF block, not an invalid API key.
The endpoint is rejecting the client's default request headers.
Try a CLI-style User-Agent such as `Codex CLI`.

这比一句:

Your API key was rejected

有用得多。

六、这次修复后的效果是什么?

修完之后,最直观的变化有 3 个。

1. 探活不再被误拦

原来:

403 Cloudflare 1010

现在:

401 API_KEY_REQUIRED

这说明请求已经进入正常鉴权层了。

2. 真正的模型请求不再继续踩同一个坑

因为主客户端默认头也一起修了,所以不只是 /models,连实际推理请求也一起规避了这类拦截。

3. 用户看到的报错终于对路了

原来是:

你的 API key 被拒绝了

现在会更接近真实原因:

这是 Cloudflare/WAF 拦截,不是无效 key

七、如果你也在做类似排查,一定记住这 3 条经验

经验 1:403 不一定等于鉴权失败

尤其是第三方聚合接口、自建转发层、OpenAI 兼容网关。

403 可能来自:

  • WAF
  • Cloudflare
  • 反爬
  • 浏览器完整性检查
  • 代理层策略

而不是业务鉴权本身。

经验 2:一定要做“同 URL、同 key、不同请求头”的对比实验

这是最快确认根因的方法之一。

不要一上来就改配置、换 key、重装环境。

先做对比实验,结论往往非常快就出来了。

经验 3:不要只修健康检查

很多系统有“检查链路”和“真实请求链路”两套逻辑。

如果你只修了 /models 探活,却没修真正的客户端请求头,问题一定会复发。

八、给 AI 改这个问题时,正确提问方式是什么?

如果你要让 AI 帮你修,不要只说:

帮我修 Hermes 403

这样很容易得到一个方向错误的答案。

更好的问法应该是:

这不是 key 失效,而是 Cloudflare/WAF 拦截了默认 Python/OpenAI 请求头。
请同时检查:

1. custom endpoint 的 `/v1/models` 探活
2. 真正的 OpenAI 客户端默认头
3. 403 错误分类逻辑
4. auth/status 最终提示文案

要求:
- 给 custom endpoint 请求加 CLI 风格 `User-Agent`
- 把 `Cloudflare 1010 / Your request was blocked` 从 auth failure 里单独识别出来
- 不要再把这类 403 提示成 API key 被拒绝

这样 AI 才更容易一次性改对。

九、最后总结

这次 Hermes Agents 的 403 修复,最容易误导人的地方就在于:

表面上看是 auth 问题,实际上是请求头被 Cloudflare 拦截。

如果你也遇到下面这些关键词:

  • 403
  • Your request was blocked
  • Cloudflare 1010
  • Access denied

请第一时间怀疑:

这是不是默认请求头被 WAF 拦了?

而不是一上来就怀疑 key。


十、一句话版本

Hermes Agents 自定义 endpoint 的 403,大概率不是 key 无效,而是 Cloudflare/WAF 把默认 Python/OpenAI 请求头拦了;正确修法不是只改健康检查,而是同时修探活请求、真实客户端默认头、403 分类和最终提示文案。

如果你也在踩这个坑,建议直接收藏这篇,少走两个小时弯路。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐