🔥 前端请求返回 HTTP Status 0?这个坑 90% 的开发者都会踩!

🔥 前端请求返回 HTTP Status 0?这个坑 90% 的开发者都会踩!

OIP-C2

服务器明明好好的,前端却报错:status 0?问题可能就藏在你忽略的那一行配置里。

💡 问题现象

最近在项目开发中遇到了一个诡异的问题:

  • ✅ 后端 API 完全正常
  • ✅ CORS 配置正确无误
  • ✅ Cookie 中有有效 token
  • ✅ 手动测试都能成功
  • 但前端页面请求就是返回 HTTP Status 0
// 前端控制台报错
API 响应不是有效的 JSON: http://localhost:9421/api/v1/media?page=1&per_page=20, status: 0

🐛 排查过程

第一步:验证后端

直接在浏览器访问 API,成功返回数据!说明后端没问题。

curl http://localhost:9421/api/v1/media/?page=1&per_page=20
# ✅ 返回正常 JSON 数据

第二步:检查 CORS

测试 OPTIONS 预检请求,完美通过

// CORS 头都正确
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Credentials: true

第三步:验证认证

Cookie 中有有效的 JWT token,手动发起请求测试:

// ✅ 成功
fetch('http://localhost:9421/api/v1/media/?page=1&per_page=20', {
  credentials: 'include',
  headers: {
    'Authorization': 'Bearer xxx'
  }
})
// Status: 200 ✓

第四步:排除插件干扰

为了彻底排除浏览器插件(如广告拦截器、代理扩展等)对请求的干扰,我们在无痕模式下禁用了所有插件和脚本,再次发起请求。

// 在无痕模式下,所有扩展默认禁用
fetch('http://localhost:9421/api/v1/media/?page=1&per_page=20', {
  credentials: 'include'
})
// 结果:仍然返回 status 0

确认问题与插件无关,继续深入调试。

第五步:发现问题

继续深入调试,发现了一个关键信息:

Response {
  status: 0,
  type: "opaqueredirect",  // ← 关键!
  ok: false,
  headers: {}  // 空的!
}

opaque-redirect 是什么鬼?!

🎯 问题根源

原来,问题出在这行代码上:

// ❌ 错误的配置
const response = await fetch(url, {
  method: 'GET',
  credentials: 'include',
  redirect: 'manual'  // ← 就是这个!
});

什么是 redirect: 'manual'

Fetch API 提供了三种重定向模式:

模式 行为 使用场景
follow(默认) 自动跟随重定向 大多数情况
manual 不跟随重定向,返回 opaque-redirect 需要手动处理重定向 URL
error 遇到重定向直接报错 严格模式

为什么会返回 Status 0?

  1. 前端请求发送后,后端返回 3xx 重定向响应(比如重定向到登录页)
  2. 因为设置了 redirect: 'manual',浏览器不会跟随重定向
  3. 浏览器返回一个 opaque-redirect 类型的响应
  4. 这个响应的 status = 0headers = {},body 为空
  5. 尝试解析 JSON 时就会报错:"Unexpected end of input"

为什么会有 redirect: 'manual' 这个选项?

既然这个选项这么容易出问题,那它存在的意义是什么?

根据 Fetch 规范,redirect: 'manual' 的设计初衷主要是为了浏览器内部导航Service Worker 等特定场景。在这些场景下,开发者需要完全控制重定向的处理流程。但对于普通的前端应用代码,你几乎永远不需要用到它

换句话说:除非你在写 Service Worker 或做非常底层的网络控制,否则永远不要碰 redirect: 'manual'

✅ 解决方案

方案 1:移除 redirect: 'manual'(推荐)

// ✅ 正确的做法
const response = await fetch(url, {
  method: 'GET',
  credentials: 'include'
  // 不设置 redirect,使用默认的 'follow'
});

让浏览器自动处理重定向,99% 的情况都应该这样做!

方案 2:如果你确实需要处理重定向(但不用 manual

// 正确的重定向处理方式:在响应中判断状态码
const response = await fetch(url, {
  method: 'GET',
  credentials: 'include'
});

if (response.redirected) {
  // 重定向发生了,response.url 已经是最终 URL
  console.log('重定向到了:', response.url);
}

if (response.status === 302) {
  // 也可以手动获取重定向地址(但不推荐手动跳转)
  const location = response.headers.get('Location');
}

注意:使用默认的 follow 模式,浏览器会自动跟随重定向,你拿到的是最终响应。response.redirected 属性可以告诉你是否发生过重定向。

📝 最佳实践

✅ DO(推荐做法)

// 1. 使用默认配置
fetch('/api/data', {
  method: 'GET',
  credentials: 'include'
});

// 2. 如果需要处理认证失败,在响应中判断
fetch('/api/data', {
  credentials: 'include'
})
.then(response => {
  if (response.status === 401) {
    // 未认证,跳转到登录页
    window.location.href = '/login';
  }
  return response.json();
});

// 3. 检查是否发生了重定向
const response = await fetch('/api/data');
if (response.redirected) {
  console.log('请求被重定向到了:', response.url);
}

❌ DON'T(避免这样做)

// 1. 不要随意设置 redirect: 'manual'
fetch('/api/data', {
  redirect: 'manual'  // ❌ 除非你非常清楚后果
});

// 2. 不要这样手动处理重定向(这是错误的示范)
fetch('/api/data', {
  redirect: 'manual'
}).then(response => {
  // 这段代码几乎永远不会按预期工作!
  if (response.type === 'opaqueredirect') {
    window.location.href = response.headers.get('Location'); // headers 是空的!
  }
});

🔍 扩展:还有哪些情况会导致 Status 0?

除了 redirect: 'manual' 导致的 opaque-redirect 外,status 0 还可能由以下原因引起:

原因 说明
网络连接问题 网络断开、WiFi 信号差、飞行模式等
跨域请求被阻止 CORS 配置不当,OPTIONS 预检失败
请求被取消 页面刷新、导航导致请求被中断
协议混合问题 HTTPS 页面请求 HTTP 资源被浏览器阻止
DNS 解析失败 域名无法解析

所以,当你遇到 status 0 时,可以先按以下步骤快速排查:

  1. 打开浏览器开发者工具 → Network 面板
  2. 查看请求是否真的发出了?状态列显示什么?
  3. 如果有请求但状态为 (failed)(canceled),检查网络和 CORS
  4. 如果请求显示为 302 但代码里报 status 0,极大概率就是本文的问题

💡 一个真实案例

有开发者在微信公众号 H5 开发中遇到过类似问题:iOS 10 系统下,设置了自定义 header 后,所有请求都返回 status 0。排查发现是自定义 header 触发了 CORS 预检,而 iOS 10 对 CORS 预检的兼容性有问题,后端返回的 Access-Control-Allow-Headers: * 不被识别,导致真正的请求没有被发送。

解决方法是将 * 改为明确列出的 header 名称:

Access-Control-Allow-Headers: content-type, auth

这个案例告诉我们:status 0 是浏览器层面的“请求没发出去”或“响应被拦截”,通常不是后端业务错误,要从前端/网络层面找原因。

🎓 学到的教训

  1. 不要过度设计 - 浏览器的默认行为通常是最好的
  2. 理解 HTTP 状态码 - Status 0 不代表服务器错误,而是网络层面的问题
  3. 善用调试工具 - Network 面板能看到请求的详细信息
  4. 了解 Fetch API - 各种参数的含义和使用场景,特别是 redirect 这个容易被忽视的选项
  5. 遇到 status 0,先检查网络和配置,再怀疑后端 - 因为请求根本没到服务器

🔗 相关资源


💬 互动话题

你在开发中遇到过哪些诡异的 HTTP 错误?欢迎在评论区分享你的故事!

👍 如果这篇文章帮到了你,请点赞、在看、转发三连!


希望这篇推文能帮助更多开发者避开这个坑!🚀

posted @ 2026-04-01 17:35  Athenavi  阅读(5)  评论(0)    收藏  举报