预检请求是浏览器自动发起的options请求,用于确认跨域请求是否被允许;后端必须正确响应access-control-allow-origin等cors头且状态码为200或204,前端应避免非必要自定义头或非简单content-type以减少预检。

Ajax跨域请求遇到预检(Preflight)时,核心不是前端“绕过”它,而是前后端协同满足浏览器的CORS安全要求。预检是浏览器自动发起的OPTIONS请求,用于确认后续实际请求是否被允许——它不经过你写的fetch或XMLHttpRequest逻辑,而是由浏览器底层触发,后端必须正确响应才能放行真实请求。
后端必须正确处理 OPTIONS 预检请求
当请求满足以下任一条件时,浏览器会先发OPTIONS预检:
- 使用了非简单方法(如
PUT、DELETE、PATCH) - 设置了自定义请求头(如
Authorization、X-Request-ID) -
Content-Type不是application/x-www-form-urlencoded、multipart/form-data或text/plain(例如用了application/json)
后端需对OPTIONS请求返回合法的CORS响应头,且状态码为200或204。常见关键响应头包括:
-
Access-Control-Allow-Origin:明确指定源(如https://your-app.com),不可用*配合Credentials -
Access-Control-Allow-Methods:列出允许的方法,如GET, POST, PUT, DELETE -
Access-Control-Allow-Headers:列出前端实际发送的请求头,如Content-Type, Authorization, X-CSRF-Token -
Access-Control-Allow-Credentials:若前端设了credentials: 'include',此项必须为true,且Origin不能为* -
Access-Control-Max-Age(可选):缓存预检结果,减少重复OPTIONS请求
前端避免不必要的预检
不是所有请求都触发预检。能简化就简化,降低后端负担和调试复杂度:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 优先用
GET或POST,且只传application/x-www-form-urlencoded或text/plain数据(如表单提交) - 避免在请求中添加多余自定义头;如需认证,优先用
Cookie或Authorization(后者会触发预检,但属合理场景) - 若用
fetch,不显式设置Content-Type为application/json时,浏览器可能按默认行为发送简单请求;但一旦手动设了,就会触发预检
调试预检失败的常见路径
浏览器开发者工具的 Network 标签页里,先找灰色的OPTIONS请求,点开看:
- 状态码不是
200/204?说明后端没正确路由或拦截了OPTIONS - 缺少
Access-Control-Allow-Origin?后端CORS配置漏了 -
Access-Control-Allow-Headers里没有前端实际发的头?比如前端带了X-User-ID,但后端没声明它 - 控制台报错提示
No 'Access-Control-Allow-Origin' header,但OPTIONS响应里明明有?可能是响应头被反向代理(如Nginx)或中间件过滤/覆盖了
Node.js(Express)示例:基础预检响应
不要依赖第三方中间件盲目启用CORS,先理解它怎么工作:
app.options('/api/data', (req, res) => {
res.set({
'Access-Control-Allow-Origin': 'https://your-frontend.com',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE',
'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Requested-With',
'Access-Control-Allow-Credentials': 'true',
'Access-Control-Max-Age': '86400'
});
res.sendStatus(204);
});
app.put('/api/data', (req, res) => {
// 实际业务逻辑
res.json({ ok: true });
});
注意:生产环境建议用cors包并精细配置,而非全局app.use(cors()),尤其涉及凭证时。
预检不是障碍,是浏览器帮你守住的安全门。后端主动应答,前端合理收敛,配合就自然成立。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










