一、什么是预检请求(OPTIONS)?
在跨域请求中,当请求满足以下条件之一时,浏览器会自动发送一个 OPTIONS 请求作为“预检请求”:
使用了自定义的 HTTP 方法(如 PUT、DELETE 等)携带了自定义的请求头(如 X-Requested-With)请求头中包含 Content-Type 为 application/json 以外的类型
该请求的作用是确认服务器是否允许当前跨域操作,包括源(Origin)、方法(Methods)、头部(Headers)等。
二、HTTP 204 No Content 的含义与影响
HTTP 204 表示服务器成功处理了请求,但没有返回任何内容。对于 OPTIONS 预检请求而言,如果返回 204,可能意味着:
服务器正确接收并处理了 OPTIONS 请求;但未设置或错误配置 CORS 相关响应头,例如:
Header 名称作用说明Access-Control-Allow-Origin指定允许访问的源Access-Control-Allow-Methods允许使用的 HTTP 方法Access-Control-Allow-Headers允许的请求头字段Access-Control-Allow-Credentials是否允许携带凭据
三、为什么会出现 OPTIONS 返回 204 的情况?
以下是常见的几种原因分析:
1. 服务器未正确配置 CORS 头部
某些 Web 服务器(如 Nginx、Apache)或后端框架(如 Express.js、Spring Boot)如果没有显式配置 CORS 响应头,则默认不会添加这些关键信息。
2. 后端路由未处理 OPTIONS 请求
在某些轻量级后端服务中,开发者可能只编写了 GET/POST 接口,而忽略了对 OPTIONS 请求的处理逻辑。
3. 中间件或插件配置错误
使用了 CORS 插件(如 Express 的 cors 模块),但配置不完整或未启用 OPTIONS 路由支持。
4. CDN 或反向代理过滤掉了 CORS 头部
如果请求经过 CDN 或负载均衡器,它们可能移除了必要的 CORS 响应头。
四、如何诊断和解决这个问题?
可以通过以下几个步骤来排查和修复:
使用浏览器开发者工具查看网络面板中的 OPTIONS 请求详情,检查响应头是否存在 CORS 相关字段。直接通过 Postman 或 curl 发送 OPTIONS 请求模拟浏览器行为,观察服务器响应。在服务器端打印日志确认 OPTIONS 请求是否被正确路由处理。确保所有中间件都已正确配置,并且未屏蔽或修改 CORS 头部。
五、代码示例:不同技术栈的解决方案
以下是一些常见后端框架中如何正确响应 OPTIONS 请求的代码示例:
// Node.js + Express 示例
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());
app.options('/api/data', (req, res) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.sendStatus(200);
});
app.get('/api/data', (req, res) => {
res.json({ message: 'Hello from server!' });
});
app.listen(3000, () => console.log('Server running on port 3000'));
# Python Flask 示例
from flask import Flask, request
app = Flask(__name__)
@app.route('/api/data', methods=['GET', 'OPTIONS'])
def data():
if request.method == 'OPTIONS':
return '', 200, {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization'
}
return {'message': 'Success'}, 200
if __name__ == '__main__':
app.run()
六、流程图:CORS 预检请求处理流程
graph TD
A[前端发起跨域请求] --> B{是否需要预检?}
B -- 是 --> C[浏览器发送 OPTIONS 请求]
C --> D[服务器接收 OPTIONS 请求]
D --> E{是否返回正确的CORS头?}
E -- 是 --> F[继续发送主请求]
E -- 否 --> G[浏览器拦截主请求]
B -- 否 --> H[直接发送主请求]