深入理解 CORS 预检请求(Preflight):作用、原理与实践

在现代 Web 开发的跨域场景中,Preflight(预检请求) 是保障浏览器安全与资源共享的关键机制。它像一道 “安全闸门”,在实际请求发起前先验证服务器的 “许可”,确保跨域操作的合法性。本文将从作用、设计原因、实现方法和典型场景四个维度,带你彻底读懂 Preflight。

一、Preflight 是什么?它有什么作用?

Preflight 是浏览器在执行非简单跨域请求前,自动发送的一次OPTIONS 方法的 “探测请求”。它的核心作用是:

  • 询问服务器:当前前端的跨域请求(方法、自定义头、内容类型等)是否被允许;
  • 保障安全性:避免前端在不知情的情况下,向服务器发起 “危险” 的跨域操作(如修改数据的 PUT/DELETE 请求);
  • 协调跨域规则:让服务器明确告知浏览器 “允许哪些方法、哪些头、哪些源”,从而让浏览器决定是否执行后续的实际请求。

举个例子:前端想从 https://frontend.com 向 https://api.backend.com 发送一个带 X-User-Token 头的 PUT 请求(修改用户数据)。此时浏览器会先发送一个 OPTIONS 预检请求,询问 https://api.backend.com:“我能用 PUT 方法,带 X-User-Token 头访问你吗?” 只有服务器明确回复 “允许”,浏览器才会执行真正的 PUT 请求。

二、为什么需要 Preflight?浏览器的安全考量

浏览器引入 Preflight 机制,本质是为了遵循 **“同源策略”** 的安全约束,同时又能支持合理的跨域资源共享。

1. 什么是 “简单请求” 和 “非简单请求”?

浏览器将跨域请求分为两类,只有非简单请求才会触发 Preflight:

  • 简单请求:同时满足以下条件:
    • 方法是 GETPOST 或 HEAD
    • 头信息仅包含浏览器默认头(如 AcceptContent-Type 且值为 application/x-www-form-urlencodedmultipart/form-data 或 text/plain);
    • 没有自定义头(如 X-TokenAuthorization 等)。
  • 非简单请求:不满足上述任一条件的跨域请求,例如:
    • 使用 PUTDELETEPATCH 等方法;
    • 携带自定义请求头(如 X-API-KeyAuthorization);
    • Content-Type 为 application/jsontext/xml 等非简单类型。

2. 安全风险的规避

如果没有 Preflight,恶意前端可能向第三方服务器发起跨域的 DELETE 请求(删除数据)、携带敏感自定义头(如用户令牌)的请求,而服务器可能在未验证来源的情况下执行操作。Preflight 让服务器有机会 “审核” 请求的合法性,从而避免此类安全风险。

三、如何实现 Preflight 兼容?服务端配置关键

要让 Preflight 正常工作,服务端必须正确响应 OPTIONS 预检请求,并返回合法的 CORS 头信息。以下是不同技术栈的实现方案:

1. PHP 服务端配置示例

php

<?php
// 1. 允许的源(生产环境建议指定具体前端域名,如 'https://your-frontend.com')
header('Access-Control-Allow-Origin: *');

// 2. 允许的 HTTP 方法(需包含前端实际使用的非简单方法,如 PUT、DELETE 等)
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');

// 3. 允许的请求头(需包含前端所有自定义头,如 X-API-Key、Authorization 等)
header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization, X-API-Key');

// 4. 处理 OPTIONS 预检请求:直接返回 204 状态码(无需响应体)
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(204);
    exit;
}

// 5. 业务逻辑:处理实际请求(如 GET、POST 等)
// ...
?>

2. Node.js(Express 框架)配置示例

javascript

运行

const express = require('express');
const app = express();

// 全局 CORS 中间件处理 Preflight
app.use((req, res, next) => {
  // 允许的源
  res.header('Access-Control-Allow-Origin', '*');
  // 允许的方法
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  // 允许的请求头
  res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization, X-API-Key');
  
  // 处理 OPTIONS 预检请求
  if (req.method === 'OPTIONS') {
    res.sendStatus(204);
  } else {
    next();
  }
});

// 业务路由
app.get('/api/data', (req, res) => {
  res.json({ message: '跨域数据返回成功!' });
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

3. 前端无需额外操作

前端发起跨域请求时,浏览器会自动触发 Preflight,开发者无需手动发送 OPTIONS 请求。只需确保请求的方法、自定义头与服务端配置的 Access-Control-Allow-MethodsAccess-Control-Allow-Headers 匹配即可。

四、Preflight 的典型应用场景

Preflight 广泛应用于需要跨域交互的现代 Web 场景,以下是几个典型案例:

1. 前后端分离的企业应用

前端部署在 https://app.example.com,后端 API 部署在 https://api.example.com。当前端需要发送带 Authorization 令牌的 PUT 请求(修改用户信息)时,会触发 Preflight。服务端通过预检响应明确允许该操作,保障接口安全。

2. 第三方 API 集成

前端应用需调用第三方支付 API(如 https://payment.third-party.com),且请求中携带自定义头 X-Merchant-Id。此时 Preflight 会验证第三方服务器是否允许该跨域请求,避免前端发送非法请求。

3. 移动端 H5 与原生服务交互

移动端 WebView 中的 H5 页面(源为 https://h5.example.com)需向原生 App 提供的本地服务(如 http://localhost:8080)发送 POST 请求,并携带 Content-Type: application/json(非简单类型)。Preflight 会确保该跨域请求被合法执行。

总结

Preflight 是浏览器在跨域场景下的 “安全守门人”,它通过 OPTIONS 预检请求,让服务器明确声明允许的跨域规则,从而避免非法请求带来的安全风险。作为开发者,只需在服务端正确配置 CORS 响应头(尤其是 Access-Control-Allow-MethodsAccess-Control-Allow-Headers)并处理 OPTIONS 请求,就能让 Preflight 机制顺畅工作,保障跨域交互的安全性与合法性。

阿雪技术观

让我们积极投身于技术共享的浪潮中,不仅仅是作为受益者,更要成为贡献者。无论是分享自己的代码、撰写技术博客,还是参与开源项目的维护和改进,每一个小小的举动都可能成为推动技术进步的巨大力量

Embrace open source and sharing, witness the miracle of technological progress, and enjoy the happy times of humanity! Let's actively join the wave of technology sharing. Not only as beneficiaries, but also as contributors. Whether sharing our own code, writing technical blogs, or participating in the maintenance and improvement of open source projects, every small action may become a huge force driving technological progrss

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐