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 的资源时:
- 浏览器自动在请求中添加
Origin头部,标明来源。 - 服务器收到请求后,检查该
Origin是否在允许列表中。 - 如果允许,服务器在响应中加入
Access-Control-Allow-Origin头部,值为你的前端域名(或通配符*)。 - 浏览器看到这个头部后,才将响应数据交给你的 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
-
关闭所有正在运行的 Chrome 窗口。
-
打开终端,执行以下命令启动一个专用测试实例:
# 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 -
在新打开的 Chrome 窗口中访问你的页面,CORS 将被禁用。
重要提醒:此模式下浏览器完全失去跨域保护,请勿登录任何账号或访问敏感网站。
特殊场景处理
预检请求(Preflight Request)失败
当请求满足以下任一条件时,浏览器会先发送一个 OPTIONS 请求(预检):
- 使用了非简单方法(如
PUT,DELETE) - 设置了自定义头部(如
Authorization) Content-Type不是application/x-www-form-urlencoded、multipart/form-data或text/plain
解决方法:确保服务器正确响应 OPTIONS 请求,并返回必要的 CORS 头部(参考方案一中的 if (req.method === 'OPTIONS') 处理)。
凭据(Cookie)无法携带
如果你的请求需要携带认证 Cookie(如 fetch(url, { credentials: 'include' })),必须同时满足:
- 服务器返回
Access-Control-Allow-Credentials: true Access-Control-Allow-Origin*不能为 ``**,必须指定具体域名- 前端请求设置
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 头部:fetch 或 XMLHttpRequest 无法设置 Origin 或 Access-Control-* 等受保护头部,这些由浏览器自动管理。
生产环境务必验证:开发时代理有效,但部署后仍需确保真实服务器配置了正确的 CORS 策略。

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