首页/文章/八股文

OPTIONS请求条件与浏览器安全策略CORS

2025-03-24
24839 分钟
...

1. 什么时候会发送OPTIONS请求

在 Web 开发中,OPTIONS 请求是一种 HTTP 请求方法,通常与 CORS(跨源资源共享,Cross-Origin Resource Sharing) 机制相关。它主要用于预检(preflight)请求,以检查服务器是否允许特定的跨域请求。以下是详细说明:

1.1. 发送OPTIONS请求条件

1.1.1. 跨域请求且不是简单请求

当发起的请求是跨域的(即请求的目标域名与当前页面域名不同),并且不满足“简单请求”的条件时,浏览器会先发送一个 OPTIONS 请求作为预检请求,以确认服务器是否允许该跨域操作。

简单请求的条件(必须同时满足):

  • 请求方法是以下之一:

    • GET
    • HEAD
    • POST
  • 请求头仅包含以下“安全”的头:

    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type(且值限于 application/x-www-form-urlencodedmultipart/form-datatext/plain
  • 不使用自定义头(如 X-Custom-Header)。

  • 不使用 ReadableStream 等复杂数据。

触发预检的例子

  • 使用 PUTDELETE 等非简单方法。
  • 设置了自定义请求头(如 AuthorizationX-API-Key)。
  • Content-Typeapplication/json

示例

fetch('https://api.example.com/data', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json', // 非简单 Content-Type
    'X-Custom-Header': 'value',         // 自定义头
  },
  body: JSON.stringify({ key: 'value' }),
});

这种请求会触发一个 OPTIONS 预检请求。


1.1.2. 服务器要求预检

即使是简单请求,如果服务器在响应中明确要求客户端进行预检(例如通过特定的 CORS 配置),也可能触发 OPTIONS 请求。不过这种情况较少见,通常由服务器端的特殊策略决定。

1.1.3. 手动发送 OPTIONS 请求

开发者也可以通过代码手动发送 OPTIONS 请求(例如使用 fetchXMLHttpRequest),但这不是常见用法,通常用于测试服务器支持的 HTTP 方法或其他元信息。

fetch('https://api.example.com', { method: 'OPTIONS' });

1.2. OPTIONS 请求的作用

  • 询问服务器支持OPTIONS 请求会携带一些头部(如 Access-Control-Request-MethodAccess-Control-Request-Headers),询问服务器是否允许后续的实际请求。
  • 服务器响应:服务器返回支持的方法、允许的头、域名等信息,通过 Access-Control-Allow-* 头部告诉浏览器是否可以继续。

示例请求

OPTIONS /data HTTP/1.1
Host: api.example.com
Origin: http://localhost:3000
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, X-Custom-Header

示例响应

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, X-Custom-Header

1.3. 如何避免不必要的 OPTIONS 请求?

  1. 使用简单请求
    • 如果可能,限制方法为 GET/POST/HEAD,避免自定义头,使用简单的 Content-Type
  1. 服务器端优化
    • 配置 CORS 允许常用的方法和头部,减少预检频率。
    • 使用 Access-Control-Max-Age 缓存预检结果,避免重复发送 OPTIONS
  1. 调整请求设计
    • 如果业务允许,避免跨域,直接部署到同一域名下。

1.4. 总结

  • 触发条件:跨域请求 + 非简单请求(如复杂方法或自定义头)。
  • 目的:预检服务器是否允许实际请求,确保安全性。
  • 常见场景:现代前端(如 React、Vue)调用后端 API 时,涉及 application/json 或认证头。

如果你的项目中遇到了意外的 OPTIONS 请求,可以检查请求头和方法,或者告诉我具体场景,我可以帮你分析!

2. 什么是CORS?如何解决?

2.1. 什么是 CORS?

CORS(Cross-Origin Resource Sharing,跨源资源共享)是一种浏览器安全机制,用于限制网页中的脚本(如 JavaScript)从一个源(域名、协议、端口)访问另一个源的资源。它由浏览器强制执行,旨在防止恶意网站未经授权访问其他网站的数据。

2.1.1. 核心概念

  • 同源策略(Same-Origin Policy)

    • 浏览器的默认安全策略,只允许脚本访问与当前页面同源的资源。
    • 同源:协议(http/https)、域名(example.com)、端口(80/443)完全相同。
    • 示例:
      • http://example.comhttp://example.com/api 是同源。
      • http://example.comhttps://example.comhttp://api.example.com 不同源。
  • 跨源请求

    • 当请求的目标资源与当前页面不同源时,称为跨源请求。
    • 例如,从 http://localhost:3000 请求 https://api.example.com/data

2.1.2. CORS 如何工作

  • 当浏览器检测到跨源请求时,会根据请求类型(简单请求或非简单请求)执行不同的处理:
    1. 简单请求
      • 满足特定条件(例如使用 GETPOST,且 Content-Typeapplication/x-www-form-urlencoded 等)。
      • 浏览器直接发送请求,并在请求头中添加 Origin
      • 服务器通过 Access-Control-Allow-Origin 响应是否允许。
    1. 非简单请求(需要预检):
      • 使用复杂方法(如 PUTDELETE)或自定义头(如 Authorization)。
      • 浏览器先发送 OPTIONS 预检请求,询问服务器是否允许。
      • 服务器返回允许的方法、头等信息后,浏览器再发送实际请求。

2.1.3. 常见的 CORS 错误

  • 浏览器控制台报错:
Access to fetch at 'https://api.example.com' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
  • 原因:服务器未返回适当的 CORS 头部,或者不允许当前源。

2.2. 如何解决 CORS 问题?

解决 CORS 问题通常需要服务器端和客户端的配合,但主要依赖服务器端配置。以下是常见的解决方案:

2.2.1. 1. 服务器端解决(推荐)

服务器需要返回正确的 CORS 头部,允许跨源访问。具体的实现取决于服务器技术栈:

  • 设置 Access-Control-Allow-Origin

    • 指定允许访问的源,或者使用 * 表示允许所有源。
    • 示例(Node.js + Express):
const express = require('express');
const app = express();

app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', 'http://localhost:3000'); 
  
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  next();
});

app.get('/data', (req, res) => {
  res.json({ message: 'Hello' });
});

app.listen(5000);
  • 处理预检请求

    • 对于非简单请求,服务器需要响应 OPTIONS 请求。
    • 示例(Express):
app.options('*', (req, res) => {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  res.sendStatus(204); 
});
  • 使用现成中间件

    • Node.js/Express:使用 cors 中间件。
const cors = require('cors');
app.use(cors()); 

app.use(cors({ origin: 'http://localhost:3000' }));
    • Spring Boot (Java)
@CrossOrigin(origins = "http://localhost:3000")
@RestController
public class MyController {
    @GetMapping("/data")
    public String getData() {
        return "Hello";
    }
}
    • Nginx
location / {
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
        return 204;
    }
    add_header 'Access-Control-Allow-Origin' '*';
}

2.2.2. 2. 客户端解决(临时或开发环境)

如果无法修改服务器,或者只是开发阶段,可以使用以下方法绕过 CORS 限制:

  • 代理服务器

    • 在本地开发时,通过代理将跨域请求转发到目标服务器。
    • Webpack Dev Server 示例
module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'https://api.example.com',
        changeOrigin: true,
        pathRewrite: { '^/api': '' },
      },
    },
  },
};

请求 http://localhost:3000/api/data 会代理到 https://api.example.com/data

  • 浏览器插件

    • 使用如 “Allow CORS: Access-Control-Allow-Origin” 的 Chrome 扩展,临时禁用浏览器的 CORS 限制(仅限开发测试)。
  • JSONP(不推荐):

    • 利用 <script> 标签绕过 CORS,仅适用于 GET 请求,老技术,已被现代方案取代。

2.2.3. 3. 生产环境最佳实践

  • 避免使用 *

    • 用具体域名替换 Access-Control-Allow-Origin: *,提高安全性。
  • 支持带凭证的请求

    • 如果需要发送 Cookie 或认证头(如 Authorization),设置:
res.header('Access-Control-Allow-Origin', 'http://localhost:3000'); 
res.header('Access-Control-Allow-Credentials', 'true');

客户端也需设置 withCredentials

fetch('https://api.example.com', { credentials: 'include' });
  • 缓存预检请求

    • 添加 Access-Control-Max-Age 头部,避免重复发送 OPTIONS
res.header('Access-Control-Max-Age', '86400'); 

2.3. 总结

  • CORS 是什么:浏览器限制跨源请求的安全机制。
  • 解决方法
    1. 服务器端:配置 CORS 头部(如 Access-Control-Allow-Origin),推荐使用中间件。
    2. 客户端:开发时用代理,生产环境尽量避免绕过。
  • 关键点:根据请求类型(简单/非简单)和业务需求调整配置。

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
八股文2025-03-24
前端项目中,强缓存和协商缓存的配置
前端缓存分为HTTP缓存和 1. HTTP缓存(本文重点) 强缓存 协商缓存 2. 浏览器缓存 比较熟悉的 cookie,localstorage sessionstorage indexDB… 或者cacheStorage 请求的缓存,如果本地有取本地的 先说总结的内容 webpack配置(主要是文件名上有个hash值,便于服务器校验文件有没有修改) 使用hash的好处就是,使版本发布之后,使之前文件的强缓存失效 nginx配置 (主...
面试面试官
八股文2025-03-24
搞定前端性能优化问题
一、首屏加载优化 首屏加载是指 用户打开页面到第一个可视内容呈现出来之间的时间 。它直接影响用户对网站的第一印象,因此在前端性能优化中具有最高的优先级。 优化首屏加载的目标是 尽可能快地把关键内容展示给用户,减少白屏时间,提升首屏体验。 常见的优化方式包括资源按需加载、预加载关键资源、使用服务端渲染或静态生成等。 1\. 资源按需加载(Lazy Load) 在前端应用中,JS、CSS、图片等资源如果全部打包在一起加载,会极大拖慢页面的加...
面试面试官
八股文2025-03-19
JS宏任务,微任务,DOM渲染,requestAnimationFrame执行顺序比较
本文小结 1. 宏任务和微任务都是异步任务。 2. 优先级: 3. 慢于同步任务。本次实践中,第一次执行的时间比微任务慢,比宏任务和DOM渲染快。 来自MDN的理论部分 每个代理都是由 事件循环 驱动的,事件循环负责收集用事件(包括用户事件以及其他非用户事件等)、对任务进行排队以便在合适的时候执行回调。然后它执行所有处于等待中的 JavaScript 任务(宏任务),然后是微任务,然后在开始下一次循环之前执行一些必要的渲染和绘制操作。...
面试面试官
八股文2025-03-18
一文总结vue和react的区别
Vue.js 和 React.js 都是流行的 JavaScript 前端框架,用于构建用户界面。它们在设计理念、语法和使用方式等方面有一些区别,以下是一些比较详细的对比: 设计理念: Vue.js :Vue.js 是一款轻量级、渐进式的框架,它专注于视图层的渲染和状态管理。Vue 的核心思想是通过数据驱动视图的变化,提供了一套简洁的模板语法和响应式数据绑定机制,使得开发者可以更便捷地构建交互式的用户界面。 React.js :Reac...
面试面试官
八股文2025-03-10
最全的Map 和 WeakMap的区别
和 的区别及面试追问知识点 一、两者区别总结 (一)基本概念 和 均为 JavaScript 中用于存储键值对的数据结构,但在多个关键特性上有显著差异。 (二)键的类型限制 1. 键类型无严格限制,可接受基本数据类型(如字符串、数字、布尔值)以及引用数据类型(如对象、数组、函数)。 示例代码: 2. 键必须为对象,使用基本数据类型作为键会抛出错误。 示例代码: (三)垃圾回收机制 1. 对键和值采用强引用,只要 对象存在,其引用的键值对...
面试面试官
八股文2025-03-07
hash与history路由模式区别hash与history的区别
hash与history的区别 两种路由模式原因原因 对于 这类渐进式前端开发框架, 为了构建 SPA(单页面应用), 需要引入前端路由系统, 这也就是 存在的意义。 前端路由的核心,就在于 —— 改变视图的同时不会向后端发出请求。 因此有两种路由 1. : 即地址栏 URL 中的 符号(此 不是密码学里的散列运算)。 比如这个 URL: , 的值为 。它的特点在于:hash 虽然出现在 URL 中, 但不会被包括在 HTTP 请求中,...
面试面试官

评论

请登录后发表评论

去登录
加载评论中...

目录