如何在浏览器中调试 CORS 错误
当你触发一个 fetch 请求,打开控制台,看到类似这样的内容:
Access to fetch at ‘https://api.example.com’ from origin ‘http://localhost:3000’ has been blocked by CORS policy.
在你开始随意修改服务器头之前,先停下来。浏览器中的大多数 CORS 调试工作归结为读取浏览器已经告诉你的内容——如果你知道在哪里查看的话。
关键要点
- CORS 错误是由浏览器强制执行的,而不是服务器。请求通常会到达服务器;阻止发生在响应阶段。
- 浏览器控制台消息是你的主要诊断工具——它通常会指出失败的规则或头信息,但也可能反映底层的网络、TLS 或策略问题。
- 如果你没有注意查看,预检 OPTIONS 请求会静默失败。始终检查网络选项卡并筛选”全部”以发现它们。
- 并非每个看起来像 CORS 的错误实际上都是 CORS 问题。混合内容、TLS 失败、重定向和浏览器扩展都可能伪装成 CORS 问题。
CORS 错误的实际含义
CORS(跨源资源共享)错误发生在浏览器阻止跨源请求时,因为服务器的响应没有包含正确的权限。关键词是浏览器——请求通常会到达服务器。阻止发生在返回的路上。
源(origin)由协议、域名和端口的组合定义。因此 http://localhost:3000 和 http://localhost:4000 是不同的源,即使在同一台机器上。
如何使用开发者工具调试 CORS 错误
打开开发者工具 → 网络选项卡(F12 或 Cmd+Option+I)。重现失败的请求。然后:
- 找到失败的请求——查找红色条目或按”Fetch/XHR”筛选。
- 检查控制台选项卡——那里的错误消息是具体的。它会告诉你哪个头信息缺失或不匹配。
- 点击请求 → 头信息选项卡——检查请求头(特别是
Origin)和响应头(特别是Access-Control-Allow-Origin)。
控制台错误是你的主要诊断工具。现代 Chrome 和 Firefox 会给出精确的消息,例如:
- “No ‘Access-Control-Allow-Origin’ header is present” → 服务器根本没有发送 CORS 头。
- “The value of ‘Access-Control-Allow-Origin’ must not be the wildcard ’*’ when credentials mode is ‘include’” → 你使用了
credentials: 'include'但服务器响应了*。 - “Method PUT is not allowed by Access-Control-Allow-Methods” → 预检拒绝了你的 HTTP 方法。
调试预检请求(OPTIONS)
当你的请求使用非简单方法(PUT、DELETE、PATCH)、自定义头信息或规范允许的少数几种之外的内容类型时,浏览器会首先发送一个预检 OPTIONS 请求。如果失败,你的实际请求永远不会发出。
在网络选项卡中,按”全部”筛选,查找指向同一 URL 的 OPTIONS 请求,它出现在你的主请求之前。点击它并检查:
- 请求头:
Access-Control-Request-Method和Access-Control-Request-Headers——这些告诉服务器实际请求需要什么。 - 响应头:
Access-Control-Allow-Methods、Access-Control-Allow-Headers、Access-Control-Allow-Origin——服务器必须明确允许每一个。
如果 OPTIONS 请求返回 4xx 或 5xx 状态码,那很可能是服务器端路由或配置错误,而不是 CORS 头配置错误。首先修复底层错误。
Discover how at OpenReplay.com.
这真的是 CORS 错误吗?
并非每个表现为 CORS 的错误都是由 CORS 引起的。常见的误报包括:
- 混合内容——从
https://页面获取http://被浏览器的混合内容策略阻止,与 CORS 头无关。 - TLS/证书错误——失败的 TLS 握手在控制台中可能看起来像 CORS 失败。
- 重定向到不同的源——如果服务器将你的请求重定向到新源,浏览器会阻止该请求。CORS 请求不允许跨源重定向。
- 浏览器扩展——隐私或广告拦截扩展可能在 CORS 评估之前就取消请求。
- 第三方 Cookie 限制——如果你使用
credentials: 'include',即使你的 CORS 头完全正确,现代浏览器也可能阻止来自第三方源的 Cookie。
要隔离真正的原因,请在禁用扩展的隐私/无痕窗口中尝试该请求。
凭证请求:常见陷阱
如果你使用 fetch(url, { credentials: 'include' }),服务器不能响应 Access-Control-Allow-Origin: *。它必须回显确切的请求源。服务器还需要在其响应中包含 Access-Control-Allow-Credentials: true。缺少任何一个,浏览器都会阻止响应。
CORS 调试快速检查清单
| 检查项 | 要查找的内容 |
|---|---|
| 控制台错误消息 | 失败的确切头信息或规则 |
| 是否存在 OPTIONS 请求? | 触发了预检——检查其响应 |
响应是否有 Access-Control-Allow-Origin? | 必须匹配你的源或为 * |
| 使用凭证? | 不允许使用 *;需要确切的源 |
| 请求返回 4xx/5xx? | 服务器错误——不是 CORS 问题 |
| 混合内容? | 从 HTTPS 页面请求 HTTP——总是被阻止 |
结论
浏览器错误消息、网络选项卡和头信息面板为你提供了在五分钟内诊断大多数 CORS 问题所需的一切。阅读确切的错误,如果存在则找到 OPTIONS 请求,将发送的内容与预期的内容进行比较,然后从那里开始工作。抵制盲目向服务器添加 Access-Control-Allow-Origin: * 的冲动——首先理解浏览器要求什么,然后精确响应。
常见问题
不可以。CORS 是由浏览器根据服务器发送的头信息强制执行的。前端无法覆盖这一点。如果你无法控制服务器,可以通过添加正确头信息的后端代理来路由请求,但仅靠浏览器端代码无法绕过此限制。
Postman 不是浏览器,不强制执行同源策略。它直接发送请求而不检查响应上的 CORS 头。浏览器强制执行 CORS 以保护用户,因此在 Postman 中成功的请求如果服务器省略了所需的响应头,仍然可能被浏览器阻止。
当请求使用 GET、HEAD 或 POST 以外的方法、包含自定义头信息或设置 application/x-www-form-urlencoded、multipart/form-data 或 text/plain 以外的 Content-Type 时,会触发预检。浏览器首先发送 OPTIONS 请求以确认服务器允许实际请求。
如果你的请求包含凭证(如 Cookie 或授权头),服务器不能使用通配符值。它必须响应发出请求的确切源,并且还必须包含设置为 true 的 Access-Control-Allow-Credentials 头。通配符仅对不带凭证的请求有效。
Gain control over your UX
See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.