如何解决前端使用axios请求跨域资源的CORS问题?
问题场景
我做了个简易视频播放器,视频文件都存在和应用不同的云端服务器上。用video标签加载视频完全正常,没有CORS警告:
<video preload="metadata" @play="onPlay" @pause="onPause" :src="https://xxx.s3.amazonaws.com/sample_960x400.mp4"> </video>
但用axios发起GET请求拿同一个文件时,直接触发CORS错误:
await axios.get('https://xxx.s3.amazonaws.com/sample_960x400.mp4', { withCredentials: false, headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, PATCH, PUT, DELETE, OPTIONS', 'Access-Control-Allow-Headers': 'Origin, Content-Type, X-Auth-Token', 'Access-Control-Max-Age': 86400 } })
错误提示:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading
the remote resource at
https://xxx.s3.amazonaws.com/sample_960x400.mp4. (Reason: CORS request
did not succeed). Status code: (null).
我的后端是Node.js+Express,但视频文件不通过后端提供,直接存在云端。
疑问解答
1. 为什么video标签拿文件没CORS错误?
浏览器对不同类型资源的跨域处理逻辑不一样:<video>这类媒体标签属于**嵌入类资源,浏览器允许跨域加载这类资源,只要服务器没明确禁止嵌入(比如用X-Frame-Options头)就行。因为媒体标签只是把资源加载过来播放,不会让JS代码直接读取资源内容,不会触发同源策略要防范的安全风险,所以不需要严格的CORS校验。
2. 为什么axios会触发CORS错误?
axios发起的是AJAX类型的跨域请求,这类请求受同源策略严格管控,必须满足CORS规则:
- 你在请求头里加的
Access-Control-*系列头是服务器应该返回的响应头,不是前端该发的请求头,浏览器会直接忽略这些头;而且因为你发了非标准请求头,浏览器还会先发起OPTIONS预检请求; - 你的云端存储(这里是S3)没配置允许前端域名跨域访问,所以服务器返回的响应里没有
Access-Control-Allow-Origin这类正确的CORS响应头,浏览器就会拦截请求并报错。
3. 怎么解决这个问题?
有两种靠谱的方案:
方案一:配置云端存储的CORS规则
针对你的S3存储桶,添加允许前端域名跨域的规则,比如:- 允许的源:你的前端应用域名(生产环境别用
*,太不安全) - 允许的方法:
GET - 允许的请求头:
*或者你实际需要的头
配置好之后,S3会在响应里返回正确的CORS头,axios请求就能正常通过了。
- 允许的源:你的前端应用域名(生产环境别用
方案二:用后端做代理
既然你有Node.js+Express后端,可以写个代理接口,前端请求这个接口,后端去拉S3的视频文件再返回给前端:// Express代理接口示例 const axios = require('axios'); app.get('/proxy-video', async (req, res) => { const videoUrl = 'https://xxx.s3.amazonaws.com/sample_960x400.mp4'; try { const s3Response = await axios.get(videoUrl, { responseType: 'stream' }); res.setHeader('Content-Type', 'video/mp4'); s3Response.data.pipe(res); } catch (err) { res.status(500).send('获取视频失败'); } });前端直接请求这个代理接口就行,因为前端和后端是同源的,不会有CORS问题,而后端请求S3不受浏览器同源策略限制。
内容的提问来源于stack exchange,提问作者volume one

