文章目录

JavaScript 跨域问题:CORS 错误与解决方案

发布于 2026-04-04 02:38:19 · 浏览 2 次 · 评论 0 条

JavaScript 跨域问题:CORS 错误与解决方案

当你在浏览器中通过 JavaScript 向另一个域名发起网络请求(比如从 http://localhost:3000 请求 https://api.example.com/data),常常会遇到控制台报错:

Access to fetch at 'https://api.example.com/data' from origin 'http://localhost:3000' has been blocked by CORS policy.

这就是 CORS(跨域资源共享) 机制触发的安全限制。它并非代码错误,而是浏览器出于安全考虑主动拦截了响应。要解决这个问题,关键在于让服务器明确允许你的前端域名访问其资源


理解 CORS 的工作原理

CORS 是一种基于 HTTP 头部的机制。当你的前端代码(运行在 origin A)尝试请求 origin B 的资源时:

  1. 浏览器自动在请求中添加 Origin 头部,标明来源。
  2. 服务器收到请求后,检查该 Origin 是否在允许列表中。
  3. 如果允许,服务器在响应中加入 Access-Control-Allow-Origin 头部,值为你的前端域名(或通配符 *)。
  4. 浏览器看到这个头部后,才将响应数据交给你的 JavaScript 代码;否则直接拦截,并抛出 CORS 错误。

注意:CORS 是浏览器强制执行的策略,服务器之间的请求(如 Node.js 后端调用 API)不受影响。


常见 CORS 错误类型及对应解决方案

方案一:配置服务器返回正确的 CORS 头部(推荐)

这是最标准、最安全的解决方式。你需要拥有目标 API 服务器的控制权(或能联系到其维护者)。

修改服务器代码,在响应中添加以下头部

Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Credentials: true  (如果需要携带 Cookie)
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization

不同后端语言的具体实现如下:

Node.js (Express)

// 安装 cors 中间件:npm install cors
const cors = require('cors');
const app = express();

// 允许特定源(开发环境常用)
app.use(cors({
  origin: 'http://localhost:3000',
  credentials: true
}));

// 或者手动设置头部(更灵活)
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', 'http://localhost:3000');
  res.header('Access-Control-Allow-Credentials', 'true');
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  if (req.method === 'OPTIONS') {
    return res.sendStatus(200);
  }
  next();
});

Python (Flask)

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app, origins=['http://localhost:3000'], supports_credentials=True)

Nginx 配置

在站点配置文件中添加:

location / {
    add_header 'Access-Control-Allow-Origin' 'http://localhost:3000';
    add_header 'Access-Control-Allow-Credentials' 'true';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
    add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';

    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain charset=UTF-8';
        add_header 'Content-Length' 0;
        return 204;
    }
}

方案二:使用开发服务器代理(适用于前端开发阶段)

如果你无法修改后端服务器(例如调用第三方 API),可以在本地开发时通过代理绕过 CORS。主流前端框架都支持此功能。

React (Create React App)

在项目根目录创建 src/setupProxy.js 文件(无需导入,Webpack Dev Server 会自动加载):

const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function(app) {
  app.use(
    '/api',
    createProxyMiddleware({
      target: 'https://api.example.com',
      changeOrigin: true,
      pathRewrite: {
        '^/api': '' // 将 /api/user 重写为 /user
      }
    })
  );
};

然后前端代码改为请求本地路径:

// 原请求:fetch('https://api.example.com/data')
fetch('/api/data') // 实际被代理到 https://api.example.com/data

Vue CLI

vue.config.js 中配置:

module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'https://api.example.com',
        changeOrigin: true,
        pathRewrite: { '^/api': '' }
      }
    }
  }
}

Vite

vite.config.js 中配置:

export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'https://api.example.com',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  }
})

方案三:禁用浏览器安全策略(仅限本地测试!)

警告:此方法极度危险,绝对不可用于生产环境或日常浏览,仅作为临时调试手段。

Chrome 临时关闭 CORS

  1. 关闭所有正在运行的 Chrome 窗口

  2. 打开终端,执行以下命令启动一个专用测试实例:

    # macOS
    open -n -a "Google Chrome" --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security
    
    # Windows
    chrome.exe --user-data-dir="C:\chrome_dev_test" --disable-web-security
    
    # Linux
    google-chrome --user-data-dir=/tmp/chrome_dev_test --disable-web-security
  3. 在新打开的 Chrome 窗口中访问你的页面,CORS 将被禁用。

重要提醒:此模式下浏览器完全失去跨域保护,请勿登录任何账号或访问敏感网站。


特殊场景处理

预检请求(Preflight Request)失败

当请求满足以下任一条件时,浏览器会先发送一个 OPTIONS 请求(预检):

  • 使用了非简单方法(如 PUT, DELETE
  • 设置了自定义头部(如 Authorization
  • Content-Type 不是 application/x-www-form-urlencodedmultipart/form-datatext/plain

解决方法:确保服务器正确响应 OPTIONS 请求,并返回必要的 CORS 头部(参考方案一中的 if (req.method === 'OPTIONS') 处理)。

凭据(Cookie)无法携带

如果你的请求需要携带认证 Cookie(如 fetch(url, { credentials: 'include' })),必须同时满足:

  1. 服务器返回 Access-Control-Allow-Credentials: true
  2. Access-Control-Allow-Origin *不能为 ``**,必须指定具体域名
  3. 前端请求设置 credentials: 'include'

CORS 相关 HTTP 头部速查表

头部名称 作用 示例值
Access-Control-Allow-Origin 指定允许访问的源 http://localhost:3000*(不带凭据时)
Access-Control-Allow-Credentials 是否允许携带凭据 true
Access-Control-Allow-Methods 允许的 HTTP 方法 GET, POST, PUT
Access-Control-Allow-Headers 允许的请求头 Content-Type, Authorization
Access-Control-Expose-Headers 允许前端读取的响应头 X-Total-Count
Access-Control-Max-Age 预检请求缓存时间(秒) 86400

检查网络面板:在浏览器开发者工具的 Network 标签中,点击失败的请求,查看 Headers → Response Headers,确认是否缺少上述 CORS 头部。

不要尝试在前端代码中添加 CORS 头部fetchXMLHttpRequest 无法设置 OriginAccess-Control-* 等受保护头部,这些由浏览器自动管理。

生产环境务必验证:开发时代理有效,但部署后仍需确保真实服务器配置了正确的 CORS 策略。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文