记一次排查 OpenAI Python 客户端被 Cloudflare 拦截 (HTTP 403) 的血泪史
AI 智能体应用(如 Hermes)部署到云服务器的过程中,开发者经常会遇到一些奇怪的网络问题。今天我要分享一个非常隐蔽的坑:服务并没有封禁你的服务器 IP,却偏偏在调用时报 403 错误。
💣 案发现场:离奇的 HTTP 403
我在一台 Linux 服务器上部署了 Hermes 智能体,并通过 ~/.hermes/config.yaml 接入了部署在 Cloudflare 后的一个自定义大模型 API 接口(Endpoint: https://ip2.hackstart.org/v1)。
当我满怀期待地在终端输入 hermes 准备畅聊时,无情的红色报错出现了:
API call failed (attempt 1/3): PermissionDeniedError [HTTP 403]
🔌 Provider: custom Model: gpt-5.5
🌐 Endpoint: https://ip2.hackstart.org/v1
📝 Error: HTTP 403: Your request was blocked.
🔍 初步排查:真的是 IP 被封了吗?
看到 Your request was blocked,我的第一反应是:腾讯云服务器的机房 IP 被 Cloudflare 的 WAF(Web应用防火墙)无情拦截了。
为了验证这个猜想,我尝试利用服务器上跑着的代理服务(Clash)给 Hermes 套了一层壳,强行把流量路由到海外节点。结果——成功绕过了拦截!
似乎破案了?但作为一个对底层逻辑有洁癖的开发者,这还不够。既然套代理能行,那直连为什么不行?我决定在服务器上用最朴素的 curl 裸测一下这个 API。
# 在服务器上直接执行 curl,不带任何代理
curl -s -v https://ip2.hackstart.org/v1/models
出人意料的是,返回的并不是 Cloudflare 的拦截页面,而是:
{"code":"API_KEY_REQUIRED","message":"API key is required in Authorization header..."}
等一下,返回 401 说明请求成功打穿了 Cloudflare 到达了后端服务器! 这意味着 Cloudflare 并没有封锁这台服务器的公网 IP。
那为什么用 Python 跑的 Hermes 就会被拦?究竟是哪里出了问题?
🕵️ 锁定真凶:罪魁祸首竟是 User-Agent
为了控制变量,我写了一段极简的 Python 脚本,直接调用官方的 openai 库:
import openai
client = openai.OpenAI(base_url='https://ip2.hackstart.org/v1', api_key='sk-xxxx')
print(client.models.list())
一执行,果然,极其眼熟的报错出现了:
openai.PermissionDeniedError: Your request was blocked.
同样的服务器,用 curl 畅通无阻,用 openai 库就被拦,唯一的变量只剩下 HTTP 请求头了!
在翻阅了 openai 的源码后,我发现官方库在初始化客户端时,会强制携带专属的用户代理标识:
# openai/_base_client.py 源码片段
@property
def user_agent(self) -> str:
return f"{self.__class__.__name__}/Python {self._version}"
# 实际发送的是:User-Agent: OpenAI/Python 1.x.x
破案了!我马上用 curl 模拟了这个 User-Agent:
curl -s -v -A 'OpenAI/Python 1.40.0' https://ip2.hackstart.org/v1/models
按下回车,满屏的 Cloudflare 拦截响应(HTTP 403)立刻浮现在眼前。
结论:Cloudflare 的 Super Bot Fight Mode(机器人防御模式)默认会拦截那些看起来像“自动化脚本”或特征明显的非浏览器 User-Agent。当检测到请求头是 OpenAI/Python 时,直接判定为机器行为并一刀切拉黑。
🛠️ 终极修复方案:狸猫换太子
既然知道了是 User-Agent 惹的祸,而且由于网关是别人的平台,我们无权去 Cloudflare 后台修改 WAF 规则,那么最简单直接的解法就是:从代码底层“伪装”成正常浏览器。
找到 Hermes 所在 Python 虚拟环境下的 openai 库源码文件(路径一般为 venv/lib/python3.x/site-packages/openai/_base_client.py)。
找到 user_agent 属性,直接将其粗暴但有效地替换为浏览器的标识:
# 修改前:
def user_agent(self) -> str:
return f"{self.__class__.__name__}/Python {self._version}"
# 修改后:
def user_agent(self) -> str:
return "Mozilla/5.0"
自动化修改脚本小贴士 (Linux):
sed -i 's|f"{self.__class__.__name__}/Python {self._version}"|"Mozilla/5.0"|g' /path/to/venv/lib/python3.11/site-packages/openai/_base_client.py
💡 总结与启发
修改保存后,再次启动 Hermes,久违的对话界面顺滑弹出,没有任何延迟和拦截。
这次排查给我带来了两个重要启发:
- 别被惯性思维骗了:遇到 Cloudflare 的 403 拦截,我们总习惯性地认为是 IP 脏了,其实
User-Agent等请求头往往才是触发 WAF 风控的暗礁。 - 控制变量法永远的神:当复杂系统(如 Hermes 架构)出错时,使用最底层的工具(
curl、原生 Python 脚本)剥离一层层逻辑进行对照测试,是逼近真相最快的捷径。
希望这篇文章能帮到同样被 HTTP 403 折磨的 AI 开发者们!