预检 OPTIONS
简单请求
根据 CORS 规范,一个请求必须同时满足以下三个条件才会被视为**“简单请求”**(不触发预检):
- 方法限制:仅限
GET、POST、HEAD。 - Content-Type 限制:仅限
application/x-www-form-urlencoded、multipart/form-data或text/plain。 - 请求头限制:只能包含“受保护”的头部,如
Accept、Accept-Language、Content-Language等。
任何自定义请求头或不符合CORS 简单请求安全列表的标准请求头都会导致浏览器认为该请求具有“潜在风险”
CORS 简单请求安全列表:
| 请求头 (Header) | 限制条件与说明 |
|---|---|
| User-Agent | - |
| Referer | - |
| Host | - |
| Connection | - |
Accept | 告知服务器客户端可以处理的内容类型。通常无特殊限制。 |
Accept-Language | 告知服务器客户端理解的语言。通常无特殊限制。 |
Content-Language | 告知服务器请求体所使用的语言。通常无特殊限制。 |
Content-Type | 仅限: 1. application/x-www-form-urlencoded 2. multipart/form-data 3. text/plain |
Range | 仅限简单的字节范围值(例如 bytes=128-255)。 |
Viewport-Width | 视口宽度(客户端提示,较少手动设置)。 |
Width | 图像宽度(客户端提示,较少手动设置)。 |
DPR | 屏幕像素比(客户端提示)。 |
Save-Data | 告知服务器客户端是否开启了节流模式。 |
在实际开发中,几乎一定会触发预检:
| 行为 | 原因 |
|---|---|
| 携带 Token | 设置了 Authorization 头部。 |
| 发送 JSON | 设置了 Content-Type: application/json。 |
| 自定义 Header | 比如公司规范要求的 X-App-Version 或 X-Request-Id。 |
| 非简单方法 | 使用 PUT、DELETE、PATCH 等方法。 |
预检请求
浏览器发送非简单请求时,会认为请求“可能不安全”,因此在发送非简单请求前会先发送预检请求:
js
OPTIONS /api/xxx预检请求头会附带即将发送的非简单请求的相关信息:
yaml
# 非简单请求的源
Origin: http://www.a.com
# 非简单请求的请求方法
Access-Control-Request-Method: PUT
# 不符合非简单请求的请求头key,如果没有,将不发送
Access-Control-Request-Headers: Authorization预检响应
浏览器通过检查 预检请求(OPTIONS)的响应头 来判断服务器是否允许这次跨域请求。
yaml
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,PATCH
Access-Control-Allow-Headers如果预检通过,浏览器才会真正发实际请求。预检不通过浏览器将会报CORS错误
预检请求缓存
浏览器通常会 缓存预检请求 的结果,这样可以减少不必要的预检请求。具体来说,浏览器会缓存 CORS 预检请求的响应结果,默认缓存时间为 5分钟,你可以在响应头中使用 Access-Control-Max-Age 来设置缓存时间。
js
Access-Control-Max-Age: 3600如果预检请求已经被发送并成功响应,在缓存有效期内,浏览器就不会每次都发出预检请求,而是直接发送实际请求。这是为了减少网络开销和提高性能。
调试
如果需要停用预检请求缓存,你可以在浏览器调试工具中勾选停用缓存
