React中POST请求为什么被CORS拦截?预检请求没发出去?
在React项目里用fetch发POST请求到第三方API时,浏览器突然报CORS错误。我明明设置了headers里的Content-Type为application/json,但控制台显示”Response to preflight request doesn’t pass access control check”。
试过在headers加mode: ‘cors’和credentials: ‘omit’,但问题依旧。奇怪的是同样的代码在本地测试环境没问题,部署到生产环境就拦截了。难道预检请求没发出去?代码如下:
const sendRequest = async () => {
try {
const response = await fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
},
body: JSON.stringify({ name: 'test' })
});
return response.json();
} catch (error) {
console.error('CORS error:', error);
}
};
检查Network面板发现只有POST请求被拦截,没有看到OPTIONS预检请求。生产服务器是Nginx配置的,是不是需要特别开启预检支持?
你代码里写得没问题,但关键点在于:当请求满足「非简单请求」条件(比如POST+application/json+自定义header),浏览器会先发OPTIONS预检请求,如果这个OPTIONS请求本身没被服务器正确处理,或者返回了错误状态码/缺失CORS头,浏览器就直接拦截后续的POST请求,所以你在Network里看不到OPTIONS——它压根没发出去或者被浏览器悄悄吞了。
先确认两件事:
1. 后端(api.example.com)有没有处理OPTIONS请求?
比如Nginx里要加个location匹配OPTIONS,返回204或200,带上这些响应头:
注意
Authorization这个header必须显式加在Access-Control-Allow-Headers里,不然预检会失败——你请求里带了Bearer token,属于自定义header,必须允许。2. 检查生产环境的API是否真的支持OPTIONS请求。可以手动用curl测一下:
如果返回里没有
Access-Control-Allow-Origin或者状态码是404/500,那就是后端没配好。本地没问题可能是因为本地开发用的是webpack-dev-server这类代理,请求其实是打到localhost再转发的,天然绕过了CORS;但生产环境是直接跨域请求,CORS规则就严格生效了。
可以试试这样改Nginx配置(如果你能改后端配置的话):
add_header 'Access-Control-Allow-Origin' 'https://your-production-domain.com' always;add_header 'Access-Control-Allow-Credentials' 'true' always;add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-Requested-With' always;add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;然后处理完OPTIONS后一定要
return 204;,别让请求继续往下走。如果后端是别人管的,比如第三方API,那可能真不支持自定义header……这种情况只能让后端加白名单,或者你前端改用代理(比如自己搭个Node中间层转发请求)。
顺便吐槽一句:CORS问题90%都是后端没处理OPTIONS,不是前端的锅,别在
mode: 'cors'上浪费时间了,它默认就是cors模式。从你的描述来看,生产环境用的是Nginx,很可能是Nginx没有正确处理OPTIONS请求,或者响应头里缺少必要的CORS字段。你可以按照以下步骤排查和解决:
先检查一下Nginx的配置文件,确保它能正确处理OPTIONS请求。可以在server块里加上类似下面的配置:
这段配置的意思是,当收到OPTIONS请求时,直接返回204状态码,并添加必要的CORS响应头。
然后,再检查一下Nginx对其他请求的CORS支持,确保每个正常的请求也带上这些响应头。比如:
另外,如果你的服务端用了反向代理,确保代理层不会把OPTIONS请求过滤掉或者改写。有时候代理层会默认丢弃非GET/POST请求,这就导致预检请求根本到不了后端。
最后,补充一点,你在fetch里加的
mode: 'cors'其实是多余的,因为fetch默认就是cors模式。而credentials: 'omit'可能会导致某些需要认证的接口出问题,建议去掉试试。改完Nginx配置后记得重启服务:
sudo nginx -s reload,然后清空浏览器缓存再试一次。如果还是有问题,可以用curl命令手动模拟一个OPTIONS请求,看看返回的响应头是不是符合预期。比如这样:总之,重点是让Nginx正确处理OPTIONS请求并返回合适的CORS头。搞定这个,问题应该就解决了。