欢迎光临
我们一直在努力

new-api 接入自建中转报错 301 Moved Permanently?一个 nginx 强制 HTTPS 引发的排查实录

new-api 接入自建中转报错 301 Moved Permanently?一个 nginx 强制 HTTPS 引发的排查实录

报错一行:bad response status code 301, body: <html>…<title>301 Moved Permanently</title>…nginx…</html>
结论先行:渠道地址填了 http://,但目标站点强制 HTTPS。new-api 收到 nginx 的 301 重定向后不跟随,直接把 301 当失败抛给你。改一个字母——把渠道地址的 http:// 换成 https://——问题即解。

一、问题现象

在这里插入图片描述

最近在自建了一个 OpenAI 兼容格式的中转服务(Node.js 写的服务,监听 http://127.0.0.1:7003,前面套了一层 nginx + HTTPS 域名)之后,把它接入 new-api(one-api 系的渠道管理面板)作为上游渠道,测试渠道时直接报错:

bad response status code 301, body: <html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx</center>
</body>
</html>

第一反应是懵的:浏览器里访问这个域名一切正常,curl 也能通,怎么到了 new-api 这里就 301 了?而且我的中转服务本身是纯 JSON 接口,从来没写过返回 HTML 的代码——这段 HTML 一看就是 nginx 的默认页面,说明请求压根没到我的服务,被更前面的 nginx 拦下了。

二、排查过程

1. 先确认 301 是谁发的

报错 body 里的 <center>nginx</center> 是 nginx 默认 301 页面的典型特征。我的后端是 Node 服务,不可能返回这个页面,所以基本可以锁定:请求被 nginx 在到达后端之前就重定向了。

2. 用 curl 还原 new-api 看到的响应

直接对渠道里填写的地址原样发请求(注意:就照抄渠道地址栏里写的东西,别自己"纠正"):

curl -i http://api.example.com/health

返回:

HTTP/1.1 301 Moved Permanently
Server: nginx
Location: https://api.example.com/health
Content-Length: 162

<html>
<head><title>301 Moved Permanently</title></head>
…

石锤了:http 请求被 301 跳转到 https。响应头里的 Location: https://… 说明 nginx 收到 HTTP 请求后要求客户端改走 HTTPS。

浏览器之所以"看起来正常",是因为浏览器收到 301 会自动跟随跳转再发一次 HTTPS 请求,用户完全无感知。但 new-api 是服务端之间的转发,它的 HTTP 客户端默认不跟随重定向——对 POST 请求跟随 301 本身在语义上就有坑(方法可能被降级成 GET、请求体可能丢失),所以绝大多数 API 转发组件都选择不跟随,直接把 301 当作非 2xx 的失败响应抛出。bad response status code 301, body: … 这个报错格式正是 new-api 转发渠道请求失败时的标准输出。

3. 看一眼 nginx 配置,确认重定向来源

到服务器上打开对应站点的 nginx 配置,果然有一段教科书式的强制 HTTPS:

server {
listen 80;
server_name api.example.com;

# 所有 http 请求一律 301 跳 https
return 301 https://$host$request_uri;
}

server {
listen 443 ssl;
server_name api.example.com;
# …证书、反代到 127.0.0.1:7003 等
}

这就是全部真相:渠道地址填了 http://,命中了第一个 listen 80 的 server 块,被 return 301 弹走。

三、解决方案

把 new-api 渠道里的地址从 http:// 改成 https://,一个字母的事:

配置项修改前修改后
渠道类型 OpenAI OpenAI
Base URL http://api.example.com ❌ https://api.example.com ✅
密钥 中转服务设定的固定密钥 不变

注意两个细节:

  • 地址栏不要带 /v1。new-api 的 OpenAI 类型渠道会自己拼接 /v1/chat/completions,地址栏只填协议 + 域名(或 IP:端口)即可。如果你的服务同时接受 /v1/chat/completions 和 /chat/completions 两种路径,带了 /v1 不会 301(顶多 404),但规范写法还是不带。
  • 如果走的是内网直连、中间没有 nginx(比如 http://192.168.1.10:7003),那就不存在强制 HTTPS 的问题,http:// 反而是对的。这个坑只出现在"套了 nginx + 强制 HTTPS"的部署形态上。
  • 改完点"测试渠道",返回 {"ok":true,…} 之类的健康响应,问题解决。

    验证

    用 curl 确认修复后的地址直接返回 200:

    curl -i https://api.example.com/health
    # HTTP/2 200
    # {"ok":true,"keys":{…}}

    四、举一反三:301 排查清单

    以后再遇到 new-api(或任何不跟随重定向的转发组件)报 301 Moved Permanently,按这个清单过一遍,基本能覆盖所有场景:

  • http 被强制跳 https(本文主角):nginx listen 80 块里 return 301 https://…,或 Cloudflare 等 CDN 开了 “Always Use HTTPS”。→ 地址改 https://。
  • 域名跳转:填了裸域被 301 到 www.,或反过来(nginx/CDN 层配置的规范化跳转)。→ 照 curl 返回的 Location 头改成最终地址。
  • 去尾斜杠 / 补路径跳转:请求 http://host/api 被 301 到 http://host/api/。对 GET 无感,对转发组件就是报错。→ 按服务端要求的路径写全。
  • 填错了域名:地址栏不小心填成了别的站(尤其同 nginx 上托管多个站点时,默认 server 会接住不匹配的请求)。→ 核对域名。
  • 本地测试没这问题:http://127.0.0.1:7003 直连时没有 nginx 介入,永远不会有 301;只有上了域名 + 反代之后才会踩。
  • 一条万能排查命令记住即可:

    curl -i <new-api 渠道地址原样>/health

    返回 200 → 地址没问题,去查密钥和路径;返回 301/30x → 看 Location 头,它指向哪里就把渠道地址改成哪里(只改协议/域名部分,别把 /health 填进去)。

    五、总结

    • bad response status code 301 + nginx 默认 HTML 页面 = 请求在到达你的后端之前就被 nginx 重定向了,后端代码再怎么排查也找不到原因。
    • 根因九成九是:渠道地址写了 http://,站点强制 HTTPS。
    • 浏览器自动跟随 301 让人产生"地址没问题"的错觉,而 new-api 这类服务端转发组件不跟随重定向,301 直接暴露成报错——这其实是合理行为,POST 跟随 301 有语义陷阱。
    • 修复:渠道地址 http:// → https://。
    • 养成习惯:给 API 填 Base URL 前,先 curl -i 一下原样地址,看状态码和 Location 头,十秒钟避开半小时的坑。

    标签建议:new-api nginx 301 HTTPS API 中转 运维排错

    赞(0)
    未经允许不得转载:171主机测评 » new-api 接入自建中转报错 301 Moved Permanently?一个 nginx 强制 HTTPS 引发的排查实录
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址