CORS
CORS(Cross-Origin Resource Sharing)是浏览器与服务器通过 Origin、Access-Control-Allow-* 和可选 Preflight 协商,允许特定跨源脚本读取响应的 HTTP 机制;它放宽同源策略,但不替代认证和授权。
[!info] related notes
CORS
一句话定义
CORS 是服务器通过 HTTP 响应头告诉浏览器:
哪些跨源页面可以让自己的 JavaScript 读取这份响应。
它是对 Same-Origin / 同源策略 的受控放宽,不是服务器端身份认证。
最小模型
Browser page origin
↓
cross-origin request with Origin
↓
Server
↓
Access-Control-Allow-* response headers
↓
Browser decides whether JS may access response
关键点:
CORS 的执行者是浏览器。
服务器返回响应,但浏览器决定是否把响应暴露给前端 JavaScript。
Origin
跨源请求通常会携带:
Origin: https://app.example.com
服务器可以返回:
Access-Control-Allow-Origin: https://app.example.com
如果浏览器认为 policy 不匹配,前端脚本就不能正常读取响应。
Simple Request
部分满足特定方法、Header 和 Content-Type 条件的请求,不需要先做 CORS preflight。
浏览器可以直接发送实际请求,再根据响应中的 CORS header 判断是否把响应暴露给 JavaScript。
因此:
“被 CORS 拦截”不一定代表请求没有到达服务器。
这也是为什么有副作用的接口不能依赖 CORS 代替 CSRF/授权保护。
Preflight
对于需要额外权限确认的跨源请求,浏览器会先发:
OPTIONS /resource
Origin: https://app.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: authorization, content-type
服务器响应示例:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, PUT
Access-Control-Allow-Headers: Authorization, Content-Type
浏览器根据这次协商决定是否继续实际请求。
Preflight 的核心不是“OPTIONS 很特殊”,而是:
浏览器在真正发出某类跨源操作前先询问服务器允许的 origin、method 和 headers。
Credentials
如果跨源请求需要 Cookie 等 credentials,前端通常需要显式启用 credentials,服务端也必须允许 credentials。
这时不能简单把:
Access-Control-Allow-Origin: *
与 credentialed CORS 混用;应返回允许的具体 origin,并正确配置:
Access-Control-Allow-Credentials: true
同时要记住:
- CORS;
- Cookie SameSite;
- CSRF;
- authentication;
是不同安全边界。
CORS 不解决什么
不等于 Authentication
CORS 允许:
某个 origin 的浏览器 JavaScript 是否可读取响应。
Authentication 回答:
当前请求是谁发的。
不等于 Authorization
即使 origin 被允许,也仍然要判断用户有没有访问具体资源的权限。
不等于 CSRF 防御
即使浏览器不允许攻击者 JavaScript 读取响应,某些跨站请求仍可能被发送。
CSRF 要看浏览器自动携带凭证、SameSite、CSRF Token、Origin 验证等另一套机制。
不是前端代码单方面可修
如果服务端没有正确返回 CORS policy,前端不能通过“加一个 Access-Control-Allow-Origin request header”修复。
常见响应头
| Header | 作用 |
|---|---|
Access-Control-Allow-Origin | 允许哪个 origin |
Access-Control-Allow-Methods | Preflight 允许哪些 methods |
Access-Control-Allow-Headers | Preflight 允许哪些 request headers |
Access-Control-Allow-Credentials | 是否允许 credentialed access |
Access-Control-Expose-Headers | 哪些非 safelisted response headers 可被 JS 读取 |
Access-Control-Max-Age | Preflight 结果可缓存多久 |
调试顺序
遇到 CORS error 时优先分层:
1. 请求 URL / Origin 是否符合预期
2. 是否发生 Preflight
3. OPTIONS 是否成功到达服务端
4. ACAO / Allow-Methods / Allow-Headers 是否匹配
5. 是否携带 credentials
6. Cookie SameSite / Secure 是否允许实际携带
7. 实际 API 是否又因为 auth / permission 被拒绝
不要把所有 “fetch failed” 都直接归因到 CORS。
学习检查
不看正文回答:
- CORS 的真正执行者是谁?
- 为什么 CORS error 不一定代表实际请求没有到达服务器?
- Simple Request 与需要 Preflight 的请求最大的流程差异是什么?
- Preflight 中 Origin、Access-Control-Request-Method、Access-Control-Request-Headers 分别在询问什么?
- credentialed CORS 为什么不能简单配
Access-Control-Allow-Origin: *? - CORS、Authentication、Authorization、CSRF 分别回答什么不同问题?
- 为什么前端自己加
Access-Control-Allow-Originrequest header 不能修服务器 CORS? - 给一个带 Authorization header 的跨源 PUT 请求,画出 Preflight → Actual Request → Browser Exposure 的完整路径。