CORS错误 - 跨域资源共享问题详解
CORS(Cross-Origin Resource Sharing,跨域资源共享)是前端开发中常见的跨域访问问题。本文详细介绍 CORS 的原理、错误原因及解决方案。
什么是 CORS
CORS 是浏览器的安全机制,用于限制网页从不同源(域名、协议、端口)发起请求。浏览器会先发送预检请求(OPTIONS),确认服务器允许后才发送实际请求。
| 术语 | 说明 | 示例 |
|---|---|---|
| 同源 | 协议、域名、端口完全相同 | http://example.com/page 和 http://example.com/api |
| 跨域 | 任一要素不同 | http://example.com 和 http://api.example.com |
| Origin | 请求来源标识 | http://localhost:8080 |
常见 CORS 错误信息
| 错误信息 | 含义 |
|---|---|
Access to fetch at 'http://...' from origin 'http://...' has been blocked by CORS policy |
跨域请求被阻止 |
No 'Access-Control-Allow-Origin' header is present on the requested resource |
服务器未返回 CORS 头 |
Access-Control-Allow-Origin' must be http://... or * |
Origin 与允许的来源不匹配 |
Credentials not supported if the CORS header 'Access-Control-Allow-Credentials' is true |
不允许发送认证信息 |
解决方案一:后端设置 CORS 响应头
在服务器响应中添加 CORS 相关的 HTTP 头:
Node.js/Express
// 安装 cors 中间件
npm install cors
// 使用
const cors = require('cors');
app.use(cors()); // 允许所有来源
// 或指定来源
app.use(cors({
origin: 'http://localhost:3000',
credentials: true
}));Python/Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app, resources={r"/api/*": {"origins": "*"}})
# 或指定来源
CORS(app,
origins="http://localhost:3000",
allow_headers="Content-Type",
supports_credentials=True)Nginx 配置
# 在 server 或 location 块中添加
add_header Access-Control-Allow-Origin $http_origin;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Access-Control-Allow-Headers "Content-Type, Authorization";
add_header Access-Control-Allow-Credentials "true";
# 预检请求处理
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin $http_origin;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Access-Control-Max-Age "3600";
return 204;
}解决方案二:开发环境代理
使用开发服务器的代理功能,将请求转发到后端:
Vite 配置代理
// vite.config.js
export default {
server: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
}Webpack Dev Server 代理
// webpack.config.js
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
pathRewrite: { '^/api': '' }
}
}
}
};解决方案三:JSONP(仅 GET 请求)
// 前端
function jsonp(url, callback) {
const script = document.createElement('script');
const callbackName = 'jsonp_' + Date.now();
window[callbackName] = function(data) {
callback(data);
delete window[callbackName];
};
script.src = url + (url.includes('?') ? '&' : '?') + callback= + callbackName;
document.head.appendChild(script);
}
jsonp('http://api.example.com/data', function(data) {
console.log(data);
});解决方案四:开发阶段允许所有来源
仅限开发环境使用:
Chrome 启动参数
# Windows
chrome.exe --disable-web-security --user-data-dir=C:\temp
# Mac
open -a Google\ Chrome --args --disable-web-security --user-data-dir=/tmp/chromeVS Code 插件
安装 "Allow CORS: Access-Control-Allow-Origin" 插件
CORS 常见配置说明
| 响应头 | 说明 | 示例值 |
|---|---|---|
Access-Control-Allow-Origin |
允许的来源 | * 或 http://localhost:3000 |
Access-Control-Allow-Methods |
允许的 HTTP 方法 | GET, POST, PUT, DELETE, OPTIONS |
Access-Control-Allow-Headers |
允许的请求头 | Content-Type, Authorization |
Access-Control-Allow-Credentials |
是否允许发送 Cookie | true(需配合具体 Origin) |
Access-Control-Max-Age |
预检结果缓存时间(秒) | 86400(24小时) |
生产环境最佳实践
| 实践 | 说明 |
|---|---|
| 指定具体 Origin | 不使用 *,明确允许的域名 |
| 限制允许的方法 | 只开放实际需要的 HTTP 方法 |
| 设置合理的缓存时间 | 减少预检请求,提升性能 |
| 使用 HTTPS | 安全传输,避免中间人攻击 |