http触发预检请求条件

浏览器会在以下情况下自动发送预检请求(Preflight Request)

简单请求 vs 复杂请求

简单请求(不触发预检)

必须同时满足以下所有条件

1. HTTP 方法限制

只能是以下三种方法之一:

  • GET
  • HEAD
  • POST
2. 请求头限制

只能包含以下安全头部

  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type(但有值的限制)
  • Range(简单范围请求)
3. Content-Type 限制

如果使用 POST 且包含 Content-Type,只能是:

  • text/plain
  • multipart/form-data
  • application/x-www-form-urlencoded
4. 其他限制
  • 不能有自定义请求头
  • 不能使用 XMLHttpRequestUpload 对象注册事件监听器
  • 不能使用 ReadableStream 对象

触发预检请求的条件(复杂请求)

满足以下任意一个条件就会触发预检:

1. HTTP 方法

javascript 复制代码
// 这些方法会触发预检
fetch(url, { method: 'PUT' });
fetch(url, { method: 'DELETE' });
fetch(url, { method: 'PATCH' });
fetch(url, { method: 'CONNECT' });

2. 自定义请求头

javascript 复制代码
// 任何自定义头部都会触发预检
fetch(url, {
  headers: {
    'X-Custom-Header': 'value',        // 自定义头部
    'Authorization': 'Bearer token',   // 认证头部
    'X-Requested-With': 'XMLHttpRequest'
  }
});

3. Content-Type 不在简单值范围内

javascript 复制代码
// 这些 Content-Type 会触发预检
fetch(url, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'    // ❌ 触发预检
  }
});

fetch(url, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/xml'     // ❌ 触发预检
  }
});

fetch(url, {
  method: 'POST',
  headers: {
    'Content-Type': 'text/html'          // ❌ 触发预检
  }
});

4. 使用凭据

javascript 复制代码
// 设置凭据会触发预检
fetch(url, {
  credentials: 'include'  // 包含 cookies
});

实际示例对比

✅ 简单请求(不触发预检)

javascript 复制代码
// GET 请求
fetch('http://127.0.0.1:3333/list');

// 简单 POST
fetch('http://127.0.0.1:3333/list', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  body: 'name=john&age=30'
});

// 纯文本 POST
fetch('http://127.0.0.1:3333/list', {
  method: 'POST',
  headers: {
    'Content-Type': 'text/plain'
  },
  body: 'hello world'
});

❌ 复杂请求(触发预检)

javascript 复制代码
// JSON 数据
fetch('http://127.0.0.1:3333/list', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'  // 触发预检
  },
  body: JSON.stringify({name: 'john'})
});

// 自定义头部
fetch('http://127.0.0.1:3333/list', {
  headers: {
    'Authorization': 'Bearer token'     // 触发预检
  }
});

// PUT 方法
fetch('http://127.0.0.1:3333/list', {
  method: 'PUT',                        // 触发预检
  body: 'data'
});

预检请求的内容

当触发预检时,浏览器会发送:

http 复制代码
OPTIONS /list HTTP/1.1
Host: 127.0.0.1:3333
Origin: http://127.0.0.1:5500
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type

服务器需要响应:

http 复制代码
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://127.0.0.1:5500
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400

避免预检的技巧

如果想避免预检请求:

  1. 使用 GET/HEAD/POST 方法
  2. 避免自定义头部
  3. POST 时使用 application/x-www-form-urlencodedtext/plain
  4. 不使用认证相关头部

但在现代 Web 开发中,JSON API 很常见,所以预检请求是不可避免的,正确配置 CORS 才是解决方案。

相关推荐
0x538 分钟前
Java网络编程(二)
java·服务器·网络
微硬创新31 分钟前
老旧产线改造:耐达讯自动化16路4‑20mA转PROFINET的工程实践
人工智能·网络协议·自动化·信息与通信
西安景驰电子2 小时前
《PTP精确时间协议系列》第一篇:从原理到应用,全面解读IEEE 1588
linux·运维·服务器·开发语言·网络·windows·php
ZGi.ai2 小时前
多模型回答不稳定:路由规则与评测方法
网络·人工智能·大模型评测·多模型·模型路由·zgi·模型网关
80s7773 小时前
独享住宅IP、长效代理ip是什么?有哪些作用?
网络·网络协议·tcp/ip
IPdodo_3 小时前
静态 IP、住宅 IP 和固定出口有什么区别?开发场景选型指南
服务器·网络·网络协议·tcp/ip·代理ip
_ZHOURUI_H_4 小时前
Unity TCP底层极限拆解:粘包、半包、CRC、序列号、双缓冲到底怎么一起工作的?
网络·网络协议·tcp/ip·游戏·unity·游戏引擎·游戏开发
长江&后浪5 小时前
防火墙的描述
开发语言·网络·php
小新讲网安6 小时前
安全运营中心(SOC)建设实战:从SIEM到威胁狩猎全流程指南
网络·安全·web安全·网络安全·网安
SKYLAB016 小时前
打破智能家居生态壁垒|微喇智能 WKV553‑A 模组原生支持 Matter,解锁跨平台互联新体验
网络·智能家居