---
title: "为什么 Apifox 能调通，浏览器却报跨域？一次“CORS Error + 403”联调排坑实录 - Angryecho"
description: "在前后端联调中，很多开发者都遇到过这样一个让人抓狂的场景：同一个接口，在 Apifox 或 Postman 里请求稳如老狗，返回 200 OK；但只要放到前端页面里用浏览器发起请求，控制台立马飘红报错。"
canonical_url: "https://www.angryecho.com/c/qian_duan_she_qu/p/P2TBC1w9mMsQtePjmFx2F"
meta:
  "og:description": "在前后端联调中，很多开发者都遇到过这样一个让人抓狂的场景：同一个接口，在 Apifox 或 Postman 里请求稳如老狗，返回 200 OK；但只要放到前端页面里用浏览器发起请求，控制台立马飘红报错。"
  "og:image": "https://www.angryecho.com/_og/d/c_Post%2Ctitle_~5ZCQ5qe9572R%2Cdescription_~5ZCQ5qe95LiA5LiL77yM5bCx5LiK5ZCQ5qe9572R44CC%2CpostId_P2TBC1w9mMsQtePjmFx2F%2Crevision_~MjAyNi0xMS0wM1QwMjo1NTo1Ni4wMDQ2NzRa%2Cp_Ii9jL3FpYW5fZHVhbl9zaGVfcXUvcC9QMlRCQzF3OW1Nc1F0ZVBqbUZ4MkYi%2Cs_-nccYnGZrT5F0ixf.png"
  "og:image:height": "630"
  "og:image:type": image/png
  "og:image:width": "1200"
  "og:title": "为什么 Apifox 能调通，浏览器却报跨域？一次“CORS Error + 403”联调排坑实录"
---

> Index of every page on this site: [llms.txt](https://www.angryecho.com/llms.txt)

# **为什么 Apifox 能调通，浏览器却报跨域？一次“CORS Error + 403”联调排坑实录**

在前后端联调中，很多开发者都遇到过这样一个让人抓狂的场景：**同一个接口，在 Apifox 或 Postman 里请求稳如老狗，返回 200 OK；但只要放到前端页面里用浏览器发起请求，控制台立马飘红报错。**

打开浏览器的开发者工具（F12 -> Network），还会看到一个诡异的现象：明明代码里只发了一次请求，网络面板里却出现了**两条记录**——第一条显示 `CORS error`，紧接着第二条显示 `403 Forbidden`。

本文将通过一次真实的联调案例，彻底扒开浏览器同源策略、`OPTIONS` 预检请求以及这套“CORS + 403”组合拳背后的底层逻辑，并给出生产环境最稳妥的解决方案。

## 案发现场还原

为方便说明，我们将本次联调涉及的两个服务分别用代号表示：

- **前端服务 A（前端页面地址）：** `http://[前端服务器IP]:9006`
- **后端服务 B（目标接口地址）：** `http://[后端服务器IP]:5050/ai/workflow/token/execute`
- **现象对比：**
  - **Apifox 测试：** 直接请求后端服务 B 的目标接口，携带 Token 和 JSON 参数，正常返回数据。
  - **浏览器测试：** 在前端服务 A（ `9006` 端口）的页面中向后端服务 B（ `5050` 端口）发起请求，Network 面板先后出现两条报错记录，第一条状态为 `CORS error`，第二条状态为 `403`。

## 核心疑问一：为什么 Apifox 可以，浏览器却不行？

很多人误以为“跨域报错”是后端服务不通，或者误以为只有 Server（服务端）才能绕过跨域。其实**跨域（CORS）和** `**OPTIONS**`** 预检请求完完全全是浏览器给自己内部运行的前端 JS 代码戴上的“紧箍咒”** ，并不是 TCP/IP 或 HTTP 网络协议本身的限制。

1. **浏览器的“历史包袱”（沙盒环境限制）** 用户的浏览器里通常保存着各个网站的 Cookie 和登录凭证。如果没有同源策略（Same-Origin Policy）限制，当你不小心点开一个恶意网站时，该网站的 JS 脚本就能在后台偷偷用你的浏览器向公司内网或网银接口发送请求。因此浏览器立下规矩：只要当前网页所在的源（协议 + IP/域名 + 端口）和目标接口不一致，就必须先经过严格的安全审查。
2. **Apifox 的“特权”（脱离浏览器沙盒的原生客户端）** 常有人疑惑：“难道 Apifox、Postman、 `curl` 或是后端代码里的 `HttpClient` 在本地都是独立的 Server 吗？” 其实它们并不是 Server（被动监听端口的服务端），而是**脱离了浏览器沙盒的独立 Client（原生网络客户端 / 进程）** 。只要不是运行在“浏览器沙盒环境”里的前端网页代码，任何直接调用操作系统底层网络接口（Socket）发出的请求，都没有跨域限制：
   - `**curl**`**、Apifox 桌面端、Postman：** 它们和微信、迅雷一样，是安装在操作系统上的普通客户端软件（桌面端通常基于 Electron，内置 Node.js 环境）。当你点击“发送”时，它们直接调用操作系统的网络接口建立 TCP 连接发包，既不在浏览器沙盒里运行，也不存在泄露浏览器各站 Cookie 的风险，自然无需理会同源策略。 *(注：如果你使用的是 Apifox/Postman 的\****网页版**，网页本身同样受浏览器跨域限制，因此它们会要求你在电脑上安装一个本地 Agent 插件，由网页把请求发给本地插件，再由插件调用系统网络发包来绕过限制。)\*
   - **后端代码里的** `**HttpClient**`**：** 比如 Spring Boot 或 FastAPI 跑起来监听 `5050` 端口时，它整体是一个 Server；但当代码内部使用 `HttpClient`、 `OkHttp` 或 Python `requests` 去请求其他接口时，它在那次通信中扮演的角色也是纯粹的 **Client**，直接走操作系统网络发包，同样不受浏览器规则约束。

简单来说：**Apifox 是直接走操作系统高速路的“专车”，直奔后端；而浏览器是个极其谨慎的“保安”，在放行网页里的请求前，非要自己先派个探子去问问后端同不同意。**

## 核心疑问二：为什么一条请求变成了两条？为什么先显示 CORS，后显示 403？

在浏览器的 Network 面板中，看到第一行是 `CORS error`，第二行是 `403`，很容易让人误以为“是先发生了跨域错误，才导致的 403”。

实际上，**Network 面板默认是按“请求发起的先后顺序”排列的，而不是按“报错产生的时间”排列的。** 这两条请求在几毫秒内发生的真实剧本如下：

1. **第 1 步（登记业务请求）：** 前端 JS 执行了 `axios.post` 或 `fetch`。浏览器立刻在 Network 面板的第 1 行登记了这个 `POST` 请求。但注意，此时浏览器发现从**前端服务 A** 请求**后端服务 B** 属于跨源请求，且携带了自定义 Header（如 `Authorization`）或 `application/json`，属于“非简单请求”。于是浏览器把第 1 行的 `POST` 请求**暂时挂起（Pending），并没有真正发出去**。
2. **第 2 步（派生探路请求）：** 浏览器紧接着在第 2 行自动生成并发送了一条 `**OPTIONS**`** 预检请求（Preflight Request）** 去给后端服务 B 探路。
3. **第 3 步（探路遭到 403 拒绝）：** 后端服务 B 收到了第 2 行的 `OPTIONS` 请求，直接回怼了一个 `**403 Forbidden**`（第 2 行变红，状态码定格为 403）。
4. **第 4 步（业务请求被连带掐死）：** 浏览器看到探路的 `OPTIONS` 请求拿到了 403（没有拿到预期的 200/204 和允许跨域的响应头），判定“探路失败，对方不允许跨域”，于是立刻回头把第 1 行还在排队等待的 `POST` 请求直接掐断，并在状态栏填上 `**CORS error**`。

所以真相是：**第 2 行的** `**OPTIONS**`** 请求报** `**403**`** 是“因”，第 1 行的业务请求报** `**CORS error**`** 是“果”。**

## 核心疑问三：为什么 OPTIONS 请求会报 403？

既然接口本身是通的，为什么浏览器派出去的 `OPTIONS` 探路请求会被后端服务 B 打回 `403`？通常有两个罪魁祸首：

- **原因 A：权限拦截器误杀（最常见）** W3C 标准明确规定，**浏览器发送** `**OPTIONS**`** 预检请求时，绝对不会携带业务自定义的** `**Authorization**`**（Token）认证头**。如果后端服务 B 的登录拦截器（如 Spring Security、Shiro 或自定义网关）一视同仁地拦截所有请求，看到 `OPTIONS` 没带 Token，就会当成未授权访问，直接拦截并返回 `401` 或 `403`。
- **原因 B：后端 CORS 白名单拦截** 浏览器发请求时会强制塞入一个请求头 `Origin: http://[前端服务器IP]:9006`（而 Apifox 默认不带这个头）。如果后端框架配置了 `CorsFilter`，但允许的域名白名单里没有包含前端服务 A 的地址，后端的跨域过滤器就会直接拒绝该请求，返回 `403 Forbidden`（响应体里通常会写着 `Invalid CORS request`）。

*小技巧：在 Apifox 中把请求方式改为* *`OPTIONS`**，并在 Header 中手动加上* *`Origin: http://[前端服务器IP]:9006`**，你就能在 Apifox 里完美复现出一模一样的* *`403`** 报错。*

## 终极解决方案

搞清楚了原理，解决问题就变得非常清晰。工程实践中主要有两条路径：**要么在前端侧用代理“绕过”浏览器的跨域判定，要么在后端侧给** `**OPTIONS**`** 请求“开绿灯”。**

### 方案一：Nginx 反向代理（最推荐，生产环境标准做法）

既然跨域是浏览器沙盒的限制，而服务端进程（Nginx）向后端发起的转发请求属于操作系统层面的客户端通信、没有跨域限制，我们只需要让前端页面请求自己的同源地址（前端服务 A），再由前端服务器的 Nginx 转发给目标接口（后端服务 B）即可。这样浏览器认为没有跨域，**连** `**OPTIONS**`** 预检请求都不会发送**。

1. **修改前端请求地址** 将前端代码中的请求地址从 `http://[后端服务器IP]:5050/ai/workflow/token/execute` 改为同源路径或相对路径： `/ai/workflow/token/execute`
2. **配置前端服务 A 所在服务器的 Nginx** 在监听 `9006` 端口的 `server` 块中添加转发规则：

   ```
    server {
        listen 9006;
        server_name [前端服务器IP或域名];

        location /ai/ {
            proxy_pass http://[后端服务器IP]:5050/ai/;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

            # 若涉及 AI 接口流式输出（SSE），建议关闭缓冲防止卡顿
            proxy_buffering off;
            proxy_read_timeout 300s;
        }
    }
   ```

3. **重载生效** 执行 `nginx -t && nginx -s reload`。此时再看浏览器 Network 面板，只会发出一条干净的 `POST` 请求，状态码直接变为 `200`。

### 方案二：在后端网关或代码中放行 OPTIONS 并配置 CORS

如果不使用前端反向代理，让浏览器直接跨域请求后端服务 B，则必须在后端同时做到两件事：**返回 CORS 允许响应头** + **无条件放行** `**OPTIONS**`** 请求**。

- **做法 1：在后端服务 B 的 Nginx 网关层拦截处理**

  ```
  location / {
      add_header 'Access-Control-Allow-Origin' 'http://[前端服务器IP]:9006' always;
      add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
      add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-Requested-With, token' always;
      add_header 'Access-Control-Allow-Credentials' 'true' always;

      # 核心：遇到 OPTIONS 预检请求直接返回 204，不让它进入后端的 Token 校验逻辑
      if ($request_method = 'OPTIONS') {
          return 204;
      }

      proxy_pass http://127.0.0.1:5050;
  }
  ```

- **做法 2：在后端服务 B 的代码中配置（以 Spring Boot 为例）** 不仅要配置全局 `CorsFilter` 允许 `http://[前端服务器IP]:9006`，还必须在自定义 Token 拦截器的最外层放行 `OPTIONS`：

  ```
  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
      // 预检请求不携带 Token，直接放行
      if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
          return true;
      }
      // 下面再走正常的 Token 校验逻辑...
  }
  ```

## 结语

下次再遇到“Apifox 能通、浏览器报错，且伴随着一条 CORS 和一条 403”时，不用再怀疑人生：

- **Apifox 能通**，因为它作为脱离浏览器沙盒的原生 Client，直接走操作系统网络发包，证明后端服务 B 的业务接口本身没问题；
- **浏览器报 CORS + 403**，说明浏览器沙盒自动发出的无 Token `OPTIONS` 预检请求撞上了后端的鉴权墙或跨域白名单墙。

优先采用 **Nginx 反向代理** 让前后端在浏览器视角下保持“同源”，既能彻底消灭多余的 `OPTIONS` 预检请求、提升接口首包响应速度，也能避免在后端安全框架中到处打补丁，是最优雅的工程实践。

排序方式 :

### **成为第一条评论！**

Nobody's responded to this post yet. Add your thoughts and get the conversation going.

```json
{"@context":"https://schema.org","@graph":[{"@id":"https://www.angryecho.com/c/qian_duan_she_qu/p/P2TBC1w9mMsQtePjmFx2F#post","@type":"DiscussionForumPosting","commentCount":0,"dateModified":"2026-11-03T02:55:56.004Z","datePublished":"2026-11-02T01:18:29.735Z","description":"在前后端联调中，很多开发者都遇到过这样一个让人抓狂的场景：同一个接口，在 Apifox 或 Postman 里请求稳如老狗，返回 200 OK；但只要放到前端页面里用浏览器发起请求，控制台立马飘红报错。","headline":"为什么 Apifox 能调通，浏览器却报跨域？一次“CORS Error + 403”联调排坑实录","isPartOf":"https://www.angryecho.com/c/qian_duan_she_qu","mainEntityOfPage":"https://www.angryecho.com/c/qian_duan_she_qu/p/P2TBC1w9mMsQtePjmFx2F","text":"在前后端联调中，很多开发者都遇到过这样一个让人抓狂的场景：同一个接口，在 Apifox 或 Postman 里请求稳如老狗，返回 200 OK；但只要放到前端页面里用浏览器发起请求，控制台立马飘红报错。\n\n打开浏览器的开发者工具（F12 -> Network），还会看到一个诡异的现象：明明代码里只发了一次请求，网络面板里却出现了两条记录——第一条显示 CORS error，紧接着第二条显示 403 Forbidden。\n\n本文将通过一次真实的联调案例，彻底扒开浏览器同源策略、OPTIONS 预检请求以及这套“CORS + 403”组合拳背后的底层逻辑，并给出生产环境最稳妥的解决方案。\n\n案发现场还原\n\n为方便说明，我们将本次联调涉及的两个服务分别用代号表示：\n\n\n\n\n\n前端服务 A（前端页面地址）： http://[前端服务器IP]:9006\n\n\n\n后端服务 B（目标接口地址）： http://[后端服务器IP]:5050/ai/workflow/token/execute\n\n\n\n现象对比：\n\n\n\n\n\nApifox 测试： 直接请求后端服务 B 的目标接口，携带 Token 和 JSON 参数，正常返回数据。\n\n\n\n浏览器测试： 在前端服务 A（9006 端口）的页面中向后端服务 B（5050 端口）发起请求，Network 面板先后出现两条报错记录，第一条状态为 CORS error，第二条状态为 403。\n\n核心疑问一：为什么 Apifox 可以，浏览器却不行？\n\n很多人误以为“跨域报错”是后端服务不通，或者误以为只有 Server（服务端）才能绕过跨域。其实跨域（CORS）和 OPTIONS 预检请求完完全全是浏览器给自己内部运行的前端 JS 代码戴上的“紧箍咒” ，并不是 TCP/IP 或 HTTP 网络协议本身的限制。\n\n\n\n\n\n浏览器的“历史包袱”（沙盒环境限制） 用户的浏览器里通常保存着各个网站的 Cookie 和登录凭证。如果没有同源策略（Same-Origin Policy）限制，当你不小心点开一个恶意网站时，该网站的 JS 脚本就能在后台偷偷用你的浏览器向公司内网或网银接口发送请求。因此浏览器立下规矩：只要当前网页所在的源（协议 + IP/域名 + 端口）和目标接口不一致，就必须先经过严格的安全审查。\n\n\n\nApifox 的“特权”（脱离浏览器沙盒的原生客户端） 常有人疑惑：“难道 Apifox、Postman、curl 或是后端代码里的 HttpClient 在本地都是独立的 Server 吗？” 其实它们并不是 Server（被动监听端口的服务端），而是脱离了浏览器沙盒的独立 Client（原生网络客户端 / 进程） 。只要不是运行在“浏览器沙盒环境”里的前端网页代码，任何直接调用操作系统底层网络接口（Socket）发出的请求，都没有跨域限制：\n\n\n\n\n\ncurl、Apifox 桌面端、Postman： 它们和微信、迅雷一样，是安装在操作系统上的普通客户端软件（桌面端通常基于 Electron，内置 Node.js 环境）。当你点击“发送”时，它们直接调用操作系统的网络接口建立 TCP 连接发包，既不在浏览器沙盒里运行，也不存在泄露浏览器各站 Cookie 的风险，自然无需理会同源策略。 (注：如果你使用的是 Apifox/Postman 的*网页版，网页本身同样受浏览器跨域限制，因此它们会要求你在电脑上安装一个本地 Agent 插件，由网页把请求发给本地插件，再由插件调用系统网络发包来绕过限制。)*\n\n\n\n后端代码里的 HttpClient： 比如 Spring Boot 或 FastAPI 跑起来监听 5050 端口时，它整体是一个 Server；但当代码内部使用 HttpClient、OkHttp 或 Python requests 去请求其他接口时，它在那次通信中扮演的角色也是纯粹的 Client，直接走操作系统网络发包，同样不受浏览器规则约束。\n\n简单来说：Apifox 是直接走操作系统高速路的“专车”，直奔后端；而浏览器是个极其谨慎的“保安”，在放行网页里的请求前，非要自己先派个探子去问问后端同不同意。\n\n核心疑问二：为什么一条请求变成了两条？为什么先显示 CORS，后显示 403？\n\n在浏览器的 Network 面板中，看到第一行是 CORS error，第二行是 403，很容易让人误以为“是先发生了跨域错误，才导致的 403”。\n\n实际上，Network 面板默认是按“请求发起的先后顺序”排列的，而不是按“报错产生的时间”排列的。 这两条请求在几毫秒内发生的真实剧本如下：\n\n\n\n\n\n第 1 步（登记业务请求）： 前端 JS 执行了 axios.post 或 fetch。浏览器立刻在 Network 面板的第 1 行登记了这个 POST 请求。但注意，此时浏览器发现从前端服务 A 请求后端服务 B 属于跨源请求，且携带了自定义 Header（如 Authorization）或 application/json，属于“非简单请求”。于是浏览器把第 1 行的 POST 请求暂时挂起（Pending），并没有真正发出去。\n\n\n\n第 2 步（派生探路请求）： 浏览器紧接着在第 2 行自动生成并发送了一条 OPTIONS 预检请求（Preflight Request） 去给后端服务 B 探路。\n\n\n\n第 3 步（探路遭到 403 拒绝）： 后端服务 B 收到了第 2 行的 OPTIONS 请求，直接回怼了一个 403 Forbidden（第 2 行变红，状态码定格为 403）。\n\n\n\n第 4 步（业务请求被连带掐死）： 浏览器看到探路的 OPTIONS 请求拿到了 403（没有拿到预期的 200/204 和允许跨域的响应头），判定“探路失败，对方不允许跨域”，于是立刻回头把第 1 行还在排队等待的 POST 请求直接掐断，并在状态栏填上 CORS error。\n\n所以真相是：第 2 行的 OPTIONS 请求报 403 是“因”，第 1 行的业务请求报 CORS error 是“果”。\n\n核心疑问三：为什么 OPTIONS 请求会报 403？\n\n既然接口本身是通的，为什么浏览器派出去的 OPTIONS 探路请求会被后端服务 B 打回 403？通常有两个罪魁祸首：\n\n\n\n\n\n原因 A：权限拦截器误杀（最常见） W3C 标准明确规定，浏览器发送 OPTIONS 预检请求时，绝对不会携带业务自定义的 Authorization（Token）认证头。如果后端服务 B 的登录拦截器（如 Spring Security、Shiro 或自定义网关）一视同仁地拦截所有请求，看到 OPTIONS 没带 Token，就会当成未授权访问，直接拦截并返回 401 或 403。\n\n\n\n原因 B：后端 CORS 白名单拦截 浏览器发请求时会强制塞入一个请求头 Origin: http://[前端服务器IP]:9006（而 Apifox 默认不带这个头）。如果后端框架配置了 CorsFilter，但允许的域名白名单里没有包含前端服务 A 的地址，后端的跨域过滤器就会直接拒绝该请求，返回 403 Forbidden（响应体里通常会写着 Invalid CORS request）。\n\n小技巧：在 Apifox 中把请求方式改为 OPTIONS，并在 Header 中手动加上 Origin: http://[前端服务器IP]:9006，你就能在 Apifox 里完美复现出一模一样的 403 报错。\n\n终极解决方案\n\n搞清楚了原理，解决问题就变得非常清晰。工程实践中主要有两条路径：要么在前端侧用代理“绕过”浏览器的跨域判定，要么在后端侧给 OPTIONS 请求“开绿灯”。\n\n方案一：Nginx 反向代理（最推荐，生产环境标准做法）\n\n既然跨域是浏览器沙盒的限制，而服务端进程（Nginx）向后端发起的转发请求属于操作系统层面的客户端通信、没有跨域限制，我们只需要让前端页面请求自己的同源地址（前端服务 A），再由前端服务器的 Nginx 转发给目标接口（后端服务 B）即可。这样浏览器认为没有跨域，连 OPTIONS 预检请求都不会发送。\n\n\n\n\n\n修改前端请求地址 将前端代码中的请求地址从 http://[后端服务器IP]:5050/ai/workflow/token/execute 改为同源路径或相对路径： /ai/workflow/token/execute\n\n\n\n配置前端服务 A 所在服务器的 Nginx 在监听 9006 端口的 server 块中添加转发规则：\n\n server {\n     listen 9006;\n     server_name [前端服务器IP或域名];\n\n     location /ai/ {\n         proxy_pass http://[后端服务器IP]:5050/ai/;\n         proxy_set_header Host $host;\n         proxy_set_header X-Real-IP $remote_addr;\n         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n\n         # 若涉及 AI 接口流式输出（SSE），建议关闭缓冲防止卡顿\n         proxy_buffering off;\n         proxy_read_timeout 300s;\n     }\n }\n\n\n\n重载生效 执行 nginx -t && nginx -s reload。此时再看浏览器 Network 面板，只会发出一条干净的 POST 请求，状态码直接变为 200。\n\n方案二：在后端网关或代码中放行 OPTIONS 并配置 CORS\n\n如果不使用前端反向代理，让浏览器直接跨域请求后端服务 B，则必须在后端同时做到两件事：返回 CORS 允许响应头 + 无条件放行 OPTIONS 请求。\n\n\n\n\n\n做法 1：在后端服务 B 的 Nginx 网关层拦截处理\n\nlocation / {\n    add_header 'Access-Control-Allow-Origin' 'http://[前端服务器IP]:9006' always;\n    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;\n    add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-Requested-With, token' always;\n    add_header 'Access-Control-Allow-Credentials' 'true' always;\n\n    # 核心：遇到 OPTIONS 预检请求直接返回 204，不让它进入后端的 Token 校验逻辑\n    if ($request_method = 'OPTIONS') {\n        return 204;\n    }\n\n    proxy_pass http://127.0.0.1:5050;\n}\n\n\n\n做法 2：在后端服务 B 的代码中配置（以 Spring Boot 为例） 不仅要配置全局 CorsFilter 允许 http://[前端服务器IP]:9006，还必须在自定义 Token 拦截器的最外层放行 OPTIONS：\n\n@Override\npublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {\n    // 预检请求不携带 Token，直接放行\n    if (\"OPTIONS\".equalsIgnoreCase(request.getMethod())) {\n        return true;\n    }\n    // 下面再走正常的 Token 校验逻辑...\n}\n\n结语\n\n下次再遇到“Apifox 能通、浏览器报错，且伴随着一条 CORS 和一条 403”时，不用再怀疑人生：\n\n\n\n\n\nApifox 能通，因为它作为脱离浏览器沙盒的原生 Client，直接走操作系统网络发包，证明后端服务 B 的业务接口本身没问题；\n\n\n\n浏览器报 CORS + 403，说明浏览器沙盒自动发出的无 Token OPTIONS 预检请求撞上了后端的鉴权墙或跨域白名单墙。\n\n优先采用 Nginx 反向代理 让前后端在浏览器视角下保持“同源”，既能彻底消灭多余的 OPTIONS 预检请求、提升接口首包响应速度，也能避免在后端安全框架中到处打补丁，是最优雅的工程实践。","url":"https://www.angryecho.com/c/qian_duan_she_qu/p/P2TBC1w9mMsQtePjmFx2F","author":{"@type":"Person","name":"皮蛋小精灵","url":"https://www.angryecho.com/u/F2Opi_dan_xiao_jing_"},"interactionStatistic":[{"@type":"InteractionCounter","interactionType":"https://schema.org/LikeAction","userInteractionCount":0},{"@type":"InteractionCounter","interactionType":"https://schema.org/DislikeAction","userInteractionCount":0},{"@type":"InteractionCounter","interactionType":"https://schema.org/ViewAction","userInteractionCount":0},{"@type":"InteractionCounter","interactionType":"https://schema.org/ShareAction","userInteractionCount":0}]}]}
```

## Sitemap

See the full [sitemap](https://www.angryecho.com/sitemap.md) for all pages.
