
如何修复 CORS 错误
CORS 是一种浏览器安全特性,而非服务器限制。你的服务器正常接收请求——但浏览器阻止响应到达你的 JavaScript。CORS 错误永远不会出现在 curl、Postman 或服务器到服务器的调用中。
同源策略(Same-Origin Policy)阻止 https://app.example.com 上的 JavaScript 读取来自 https://api.other.com 的响应,除非 API 通过 CORS 头部明确允许。

阅读错误信息
Chrome:Access to fetch at 'https://api.example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present.
常见变体:
- "No Access-Control-Allow-Origin header" → 服务器未发送 CORS 头部
- "Value not equal to supplied origin" → 配置的源不正确
- "Response to preflight doesn't pass access control check" → OPTIONS 请求失败
- "Allow-Credentials must be true" → 未启用凭据
修复 1:在服务器上添加 CORS 头部
Express / Node.js
const cors = require('cors');
// 允许特定源(生产环境)
app.use(cors({
origin: 'https://app.example.com',
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 86400,
}));
// 多个允许的源
const allowedOrigins = ['https://app.example.com', 'http://localhost:3000'];
app.use(cors({
origin: (origin, callback) => {
if (!origin || allowedOrigins.includes(origin)) callback(null, true);
else callback(new Error('Not allowed by CORS'));
},
credentials: true,
}));

Django
# pip install django-cors-headers
INSTALLED_APPS = [..., 'corsheaders']
MIDDLEWARE = ['corsheaders.middleware.CorsMiddleware', ...]
CORS_ALLOWED_ORIGINS = ['https://app.example.com']
CORS_ALLOW_CREDENTIALS = True
FastAPI
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(CORSMiddleware,
allow_origins=['https://app.example.com'],
allow_credentials=True,
allow_methods=['*'],
allow_headers=['*'],
)
Nginx
location /api/ {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' 'https://app.example.com';
add_header 'Access-Control-Allow-Methods' 'GET,POST,PUT,DELETE,OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type,Authorization';
return 204;
}
add_header 'Access-Control-Allow-Origin' 'https://app.example.com' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
}

修复 2:预检请求失败
复杂请求(DELETE、PUT、自定义头部如 Authorization)会触发 OPTIONS 预检。如果 OPTIONS 返回 404 或缺少头部,实际请求永远不会发生。
// Express:在认证中间件之前处理 OPTIONS
app.options('*', cors());
app.use(cors({ ... }));
app.use(authMiddleware); // 认证在 CORS 之后
// 跳过 OPTIONS 预检的认证
function authMiddleware(req, res, next) {
if (req.method === 'OPTIONS') return next();
// 验证 token...
}
修复 3:凭据 + Cookie
fetch(url, { credentials: 'include' }); // fetch
axios.get(url, { withCredentials: true }); // axios
服务器必须使用特定源,不能使用通配符:
# 错误:Access-Control-Allow-Origin: * + Access-Control-Allow-Credentials: true
# 正确:Access-Control-Allow-Origin: https://app.example.com
# Access-Control-Allow-Credentials: true
修复 4:你无法控制的 API
通过你自己的后端代理:
app.get('/api/proxy', async (req, res) => {
const data = await fetch('https://third-party-api.com/data');
res.json(await data.json());
});
常见错误
- 重复的 CORS 头部 — Nginx 和 Express 都添加头部;浏览器拒绝重复
- 错误响应缺少 CORS — 401/500 错误可能缺少头部,使用
always标志 - 尾部斜杠不匹配 —
https://app.com和https://app.com/是不同的源 - 未重启服务器 — 浏览器可能缓存预检结果,持续
Access-Control-Max-Age秒
→ 查看 HTTP 状态码:HTTP Status Codes