预检请求仅在非简单请求时触发:必须同时满足方法限于GET/HEAD/POST、Content-Type仅限三种、无自定义头、body不为ArrayBuffer/Blob等,否则浏览器自动发送OPTIONS预检。

预检请求不是每次跨域都发生,它只在浏览器判定当前请求“不够安全”时自动发起。关键看是否属于“简单请求”——只要不满足简单请求的全部条件,就会触发 OPTIONS 预检。
什么是简单请求(不触发预检)
必须同时满足以下四点:
- 请求方法仅限 GET、HEAD 或 POST
-
Content-Type 只能是以下三者之一:
application/x-www-form-urlencoded、
multipart/form-data、
text/plain - 未设置任何自定义请求头(如
X-Auth-Token、Authorization、X-Requested-With等) - 请求体(body)不使用
ArrayBuffer、Blob、ReadableStream等非标准类型(FormData和字符串是允许的)
只要满足任一,就触发预检
以下任一情况出现,浏览器会在实际请求前先发一个 OPTIONS 请求:
将一个或多个视频上传至YouTube。当用户想“上传到YouTube”“发YouTube”“批量上传”“upload to YouTube”“post videos to YouTube”或发布到YouTube时使用此技能。
- 使用 PUT、DELETE、PATCH、TRACE、CONNECT、OPTIONS 等非简单方法
- 设置了任意非标准请求头(哪怕只是
X-My-App: v1) -
Content-Type 是
application/json、application/xml、text/csv等非简单值 - 请求携带
credentials: 'include'(如发送 Cookie),即使方法和头都合规,也会触发预检
常见误判场景
这些看似“普通”的写法,其实都会悄悄触发预检:
-
fetch('/api', { method: 'POST', headers: { 'Content-Type': 'application/json' } })—— JSON 类型必预检 -
fetch('/login', { headers: { 'Authorization': 'Bearer xxx' } })—— 即使是 GET 请求,加了 Authorization 就要预检 -
axios.post('/data', data, { withCredentials: true })—— 凭证开启即触发
怎么确认是否触发了预检?
打开浏览器开发者工具 → Network 标签页 → 发起请求后,看是否有 OPTIONS 类型的请求排在前面,且状态码为 200 / 204。如果有,说明已进入预检流程;后端必须正确响应这个 OPTIONS 请求(返回必要 CORS 头),否则实际请求不会发出。

















