本地测试时Access-Control-Allow-Origin出现不符合预期的行为
问题原因
- 浏览器默认允许跨域嵌入媒体资源:
<img>、<video>、<audio>这类静态资源嵌入标签属于「无CORS模式」的简单请求,默认不会触发CORS校验逻辑,哪怕响应头没有匹配的Access-Control-Allow-Origin,浏览器也会正常加载资源,CORS规则仅对fetch/XHR请求、或者添加了crossorigin属性的媒体元素生效。 - 当前CORS配置逻辑错误:你配置的
$cors变量返回的是当前服务的域名$server_name(也就是资源所在的www.nginx.test),而非校验请求来源的Origin头,就算触发CORS校验,这个配置也无法实现不同来源的权限控制。 - 缺少来源拦截逻辑:仅添加CORS响应头不会直接拒绝非法请求,只有当浏览器触发CORS校验时才会根据头判断是否拦截响应,对于默认不校验CORS的媒体嵌入场景,需要主动在服务端拦截非法来源的请求。
解决方案
要实现视频资源仅允许同域加载,需要同时做Referer头校验(覆盖普通嵌入场景)和CORS规则配置(覆盖带crossorigin属性的请求/JS请求场景),调整后的nginx配置如下:
http { # 定义允许访问视频资源的域名列表,$http_origin是跨域请求带的Origin头 map $http_origin $allow_origin { ~^https://www.nginx.test$ $http_origin; # 后续要加其他允许的域名可以在这里加 default ""; } server { listen 8443 ssl http2; listen [::]:8443 ssl http2; server_name www.nginx.test; # 校验Referer头,拦截跨域嵌入的普通请求 valid_referers none blocked server_names; if ($invalid_referer) { return 403; } # 针对视频资源的CORS配置,适配带crossorigin的请求 location ~* \.(mp4|webm|ogg)$ { add_header Access-Control-Allow-Origin $allow_origin always; add_header Access-Control-Allow-Methods "GET, OPTIONS" always; # 处理预检请求 if ($request_method = OPTIONS) { add_header Access-Control-Max-Age 86400; return 204; } } } # 另一个站点www.nginx1.test的配置不需要额外调整,不影响www.nginx.test的资源权限 }
配置说明
valid_referers指令会校验请求的Referer头,当前配置允许同域页面嵌入、无Referer(用户直接在地址栏访问视频)、Referer被代理/防火墙修改的请求通过,跨域嵌入的请求会直接返回403,覆盖普通<video>嵌入的场景。如果不需要允许用户直接访问视频文件,可删除none参数。- CORS配置仅允许指定域名的带
crossorigin属性的请求/JS请求加载视频资源,避免被其他站点通过JS拉取资源。 - 如果需要放开部分第三方站点的访问权限,可以在
valid_referers和map $http_origin $allow_origin里添加对应域名即可。
内容的提问来源于stack exchange,提问作者Dominik Krulak
相关产品推荐
相关产品推荐

