Laravel+Video.js播放m3u8(HLS)时.ts分片触发CORS跨域报错
问题根因
跨域拦截本质是三个配置错误共同导致:
- 前端配置冲突:错误给XHR请求添加了
Access-Control-Allow-Origin头——这个是服务端返回的响应头,前端请求携带该头会直接触发预检请求失败;同时开启了withCredentials: true和crossOrigin: "use-credentials",但后端CORS配置未开启凭据支持,和通配符允许源规则互斥。 - CORS配置覆盖不全:现有CORS规则仅匹配
api/*路径,但ts分片默认走/storage/开头的静态文件路由,完全不经过CORS中间件,不会返回跨域响应头,浏览器直接拦截响应。 - HLS地址解析逻辑错误:m3u8索引文件走
/api/video-get路由(能正常返回跨域头),但ts分片地址被解析为静态storage路径,和索引文件不同源且无跨域头,才会出现m3u8加载正常、ts分片被拦截的现象。
分步修复方案
第一步:修正前端播放器配置
删除无效配置、错误请求头和冲突的跨域参数,修正后代码如下:
import React, { useEffect, useRef, useState } from "react"; import videojs from "video.js"; import "video.js/dist/video-js.css"; const Video = () => { const videoNode = useRef(null); const [player, setPlayer] = useState(null); useEffect(() => { if (videoNode.current) { const _player = videojs(videoNode.current, { fill: true, fluid: true, autoplay: true, controls: true, preload: "metadata", crossOrigin: "anonymous", sources: [ { src: "http://127.0.0.1:8000/api/video-get/video.m3u8", type: "application/x-mpegURL", }, ], }); setPlayer(_player); return () => { if (player !== null) { player.dispose(); } }; } }, []); return ( <div data-vjs-player> <video ref={videoNode} className="video-js"></video> </div> ); }; export default Video;
注意:如果后续需要带cookie鉴权,就不能用
*作为允许源,必须明确指定前端域名,同时把后端supports_credentials设为true,两个配置二选一不能冲突。
第二步:修正Laravel CORS配置覆盖范围
把storage静态资源路径加入跨域匹配规则,修改config/cors.php:
return [ // 新增storage路径匹配,覆盖ts分片请求 'paths' => ['api/*', 'sanctum/csrf-cookie', 'storage/*'], 'allowed_methods' => ['*'], 'allowed_origins' => ['*'], 'allowed_origins_patterns' => [], 'allowed_headers' => ['*'], 'exposed_headers' => [], 'max_age' => 0, 'supports_credentials' => false, ];
修改完成后执行php artisan config:clear清除配置缓存。
第三步:修正HLS资源路由逻辑
不要把ts分片地址指向静态storage路径,统一让所有HLS资源(m3u8索引、ts分片)走同一套API路由,确保所有请求都经过CORS中间件处理,修改视频读取路由代码:
Route::get('/video-get/{media}', function ($media) { // 处理m3u8索引文件 if (str_ends_with($media, '.m3u8')) { return FFMpeg::dynamicHLSPlaylist() ->fromDisk('public') ->open($media) ->setPlaylistUrlResolver(function ($playlist) { return url('/api/video-get/' . $playlist); }) // ts分片也指向当前API路由,不返回静态storage地址 ->setMediaUrlResolver(function ($mediaFile) { return url('/api/video-get/' . $mediaFile); }); } // 处理ts分片,返回二进制流并自动附带CORS头 return Storage::disk('public')->response($media); // 加正则匹配兼容带多级目录的分片路径,避免404 })->where('media', '.*\.(m3u8|ts)$');
第四步(可选兜底):Web服务器层直接加跨域头
如果生产环境不想让ts分片请求走Laravel框架,直接用Nginx/Apache处理静态资源,可以在Web服务器配置中直接给HLS相关文件加跨域头,Nginx配置参考:
location ~* \.(m3u8|ts)$ { add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods 'GET, OPTIONS'; add_header Access-Control-Allow-Headers '*'; if ($request_method = 'OPTIONS') { return 204; } }
验证标准
修改完成后清除浏览器缓存重新测试:
- m3u8索引和ts分片请求的响应头都携带
Access-Control-Allow-Origin: * - ts分片响应体大小正常,不为空
- 播放器可正常播放、拖动进度,控制台无跨域报错
内容的提问来源于stack exchange,提问作者Rawa Hamid
相关产品推荐
相关产品推荐

