您已发出一个获取请求。网络标签页显示该请求已触发,但在控制台中却出现了一整块红色错误信息: 来自源 'https://yourapp.com' 对 'https://api.example.com' 的 fetch 请求因 CORS 策略而被阻止。
在深入解释之前——这里提供了一条最快的诊断路径。打开开发者工具 → 网络标签页 → 找到失败的请求 → 查看 响应头。如果未看到 Access-Control-Allow-Origin,则您的服务器未发送 CORS 头。这就是解决方案。本文其余部分将详细说明应发送哪些内容以及原因。
CORS 实际上是什么
CORS(跨域资源共享)是由浏览器强制执行的,而不是由您的服务器控制。您的 API 本身不会阻止跨域请求。浏览器会代表用户阻止这些请求,以防止脚本从 evil.com 读取您的银行数据。
浏览器会检查:“该 API 的响应是否告诉我允许这个来源读取它?”如果答案是否定的,它将阻止响应——即使服务器已经处理了请求并返回了 200 状态码。服务器永远不会知道客户端为何丢弃了该响应。
这在调试时非常重要:错误始终出现在客户端。服务器需要告诉浏览器“是的,这个来源是被允许的”。这就是 CORS 响应头的作用。
简单请求与预检请求
并非所有跨域请求都以相同方式处理。浏览器区分了两种类型。
简单请求 是 GET 或 POST 请求,带有纯文本或表单编码体,以及一组允许的头部。浏览器会直接发送这些请求,并检查响应中的 Access-Control-Allow-Origin.
预检请求 在请求不符合这些条件时发生——例如,当您发送一个 PUT 或 DELETE,包含一个自定义头部如 Authorization 或 Content-Type: application/json,或发送凭据时。浏览器会先向同一 URL 发送一个自动的 OPTIONS 请求。如果服务器未能以正确的 CORS 头响应该 OPTIONS 调用,您的实际请求将永远不会发出。
如果您在网络标签页中看到返回 404 或 405 的 OPTIONS 请求,这就是您的请求失败的原因。您的服务器需要为接收跨域流量的每个路由处理 OPTIONS 。
关键的头部
正确设置 CORS 头意味着理解每个响应头实际控制的内容:
Access-Control-Allow-Origin—— 哪些来源可以读取响应。可以是特定来源(https://yourapp.com)或*适用于任何来源。Access-Control-Allow-Methods—— 哪些 HTTP 方法被允许(例如,GET, POST, PUT, DELETE, OPTIONS).Access-Control-Allow-Headers—— 哪些请求头部浏览器被允许发送(例如,Authorization, Content-Type).Access-Control-Allow-Credentials—— 是否允许在请求中发送 Cookie 和认证头部。必须true明确指定。Access-Control-Max-Age—— 浏览器应缓存预检响应的秒数。
通配符陷阱
使用 Access-Control-Allow-Origin: * 是打开 API 的最快方式——但一旦添加了凭据,它就会失效。当 Access-Control-Allow-Credentials: true 被要求时,浏览器会拒绝通配符。您必须指定确切的来源:
# This will fail with credentials:
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
# This works:
Access-Control-Allow-Origin: https://yourapp.com
Access-Control-Allow-Credentials: true
如果您有多个允许的来源,请读取请求的 Origin 头部,并有条件地将其回显——不要拼接它们。
常见的 CORS 错误及其含义
浏览器错误消息通常会明确指出缺少的内容。以下是快速参考:
| 错误消息 | 含义 | 解决方案 |
|---|---|---|
| 未发现 'Access-Control-Allow-Origin' 头 | 服务器未发送任何 CORS 头 | 添加 Access-Control-Allow-Origin 添加到响应中 |
| 'Access-Control-Allow-Origin' 头的值……与提供的来源不匹配 | 来源不匹配——服务器返回了错误或包含凭据的通配符来源 | 有条件地回显请求的 Origin 头;在使用凭据时移除通配符 |
| 方法 PUT 未被 Access-Control-Allow-Methods 允许 | HTTP 方法未列在允许的方法头中 | 将缺失的方法添加到 Access-Control-Allow-Methods |
| 请求头部 'Authorization' 未被 Access-Control-Allow-Headers 允许 | 自定义头部未列入允许列表 | 将该头部添加到 Access-Control-Allow-Headers |
| 预检请求的响应未通过访问控制检查 | OPTIONS 请求返回了错误状态或缺少头部 | 明确处理 OPTIONS 请求;返回 200/204 状态码并带有正确的头部 |
| 当 CORS 头 'Access-Control-Allow-Origin' 为 '*' 时,不支持凭据 | 在使用凭据模式时使用了通配符 | 将 * 替换为明确的来源;添加 Access-Control-Allow-Credentials: true |
如何在 Express、FastAPI 和 Nginx 中配置 CORS
Express(Node.js)
const cors = require('cors');
app.use(cors({
origin: 'https://yourapp.com',
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 86400
}));
// Handle preflight for all routes
app.options('*', cors());
FastAPI(Python)
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["https://yourapp.com"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Nginx
location /api/ {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' 'https://yourapp.com';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Max-Age' 86400;
add_header 'Content-Length' 0;
return 204;
}
add_header 'Access-Control-Allow-Origin' 'https://yourapp.com';
add_header 'Access-Control-Allow-Credentials' 'true';
proxy_pass http://backend;
}
请注意 Nginx 模式:您需要 add_header 同时出现在 OPTIONS 块和主块中。位于 if 块内的头部不会传递到外部块。
您的调试清单
当遇到 CORS 错误时,请按以下步骤操作:
- 阅读完整的错误信息 —— 它会明确指出哪个头部或值是错误的。
- 检查网络标签页 —— 查看实际的响应头,而不是您认为已配置的内容。
- 检查是否存在 OPTIONS 请求 —— 如果它失败或缺失,说明您的服务器未处理预检请求。
- 确认来源完全匹配 —— 末尾斜杠、HTTP 与 HTTPS 以及端口号都至关重要。
- 如果使用凭据,请移除通配符 —— 它们是互斥的。
- 确认头部未被代理剥离 —— Nginx 和 CDN 有时会剥离或覆盖 CORS 头部。
一个容易被忽略的陷阱:如果您的 API 位于反向代理或 CDN 之后,该层可能会添加其自身的 Access-Control-Allow-Origin 头部,与您的应用服务器返回的头部冲突。当两个此类头部同时出现在响应中时,浏览器会拒绝整个响应。始终检查网络层面的原始响应头,而不是仅查看应用代码发出的内容。
另一个边缘情况:某些框架仅在匹配注册处理器的路由时才附加 CORS 头部。如果您访问的是 404 或未注册的路由,CORS 中间件可能永远不会运行,您将看到“无头部存在”的错误,即使配置看起来是正确的。请先测试一个有效的端点。
如果您需要快速生成服务器应返回的精确 CORS 头部,可以使用 IO Tools CORS 头部生成器 配置来源、方法和凭据,并输出可直接粘贴到服务器配置中的正确头部块。
关于安全性的说明
CORS 不是 API 安全机制。设置 Access-Control-Allow-Origin: * 并不会在任何有意义的攻击面意义上使您的 API 公开——curl、Postman 和服务器到服务器的调用永远不会受到 CORS 限制。只有基于浏览器的 JavaScript 才会受到限制。如果您的 API 需要认证,请在 API 层通过令牌或会话进行强制执行。CORS 头部仅告诉浏览器哪些来源被允许读取 JavaScript 返回的响应。它们与实际的访问控制是相互独立的。
有了这个背景,您就可以在安全性和开放性之间做出合理的决策,而不是出于误解的安全担忧而过度限制,或在不了解权衡的情况下完全开放。
