crossorigin引发的问题追踪
背景
上线了新的业务,域名是{cityId}.yy.com
,产品打算验收一下,结果反馈好几个同事先访问https://sh.yy.com
之后切换到https://cd.yy.com
浏览的时候出现图片加载不出来的情况。经过排查发现是js没加载成功导致懒加载没生效,外在表现就是图片没加载成功。控制台报错如下:
交代下https://j1.cdn.com.cn/
域名下的资源是放在cdn上的静态资源(资源每次上线会更新版本号,不会缓存),上线的网站域名如下https://cd.yy.com
、https://sh.yy.com
(三级域名是代表城市,前面cd代表成都,bj代表北京);
现象
当前域名为https://cd.yy.com
,打开Network;
查看https://j1.cdn.com.cn/aa.js
请求返回值,,response headers
中access-control-allow-origin:https://sh.yy.com
。也就像控制台的报错一样,与当前域名不一致导致跨域。但是我们看请求的另外一个文件https://j1.cdn.com.cn/bb.js
,这个的access-control-allow-origin:https://cd.yy.com
及当前域名,bb.js
文件请求就没有任何问题。
疑问
1.为什么我这边几个开发的电脑都没有这种问题,但是产品却大概率有这问题,有时候刷新就恢复正常了?
2.为什么https://j1.cdn.com.cn/aa.js
的response headers
中的域名不是当前域名?
3.为什么同样是j1.cdn.com.cn
域名下两个文件(aa.js和bb.js)的access-control-allow-origin
却不一样?
猜测
通过bug出现反馈的操作流程和报错信息来看,应该是cdn资源被缓存住了。由于开发这边每次看问题常年喜欢打开控制台而且勾上disable cache
,所以每次请求都没有走浏览器的缓存。将disable cache
取消并且进行切换域名操作果然复现了。那么其他疑问产生了。
疑问4:为什么浏览器的disable cache
项可以影响到后端返回的response headers
,以至于资源无法加载?
疑问5:还是说这个请求所谓的response headers
是由于资源请求有问题,浏览器自身机制直接取上次的值呢?
通过在iTerm执行命令行 curl -IL https://j1.cdn.com.cn/aa.js
,可以看到response headers
中的access-control-allow-origin
确实是https://sh.yy.com
,说明疑问5中的说法不成立,response headers
确实是服务器端返回的。
那么我们看一下这个资源请求方式有什么不同:
html中请求的script标签
看到了个后来加入的属性crossorigin="anonymous"
,添加这个属性是因为引入了监控系统,通过这个属性可以获取到资源内部详细的报错信息。对这个属性不熟悉的可以参考这里。
实践
有了以上猜测之后随即移除了aa.js
等一切js静态资源的crossorigin
属性,移除之后发现静态资源可以正常加载,查看下详细的网络请求返回的字段;
1.https://j1.cdn.com.cn/aa.js
请求返回值response headers
中access-control-allow-origin:https://sh.yy.com
。
2.https://j1.cdn.com.cn/bb.js
请求返回值response headers
中access-control-allow-origin:https://cd.yy.com
。
和报错时候返回的一致,但是浏览器并没有再报错了,资源可以正常请求;
结论
由于cdn根据资源路径缓存了response里面的内容,当我们在不同域名下请求相同资源的时候就会出现域名不一致的问题;如果标签中添加了crossorigin="anonymous"
属性,那么跨域请求的时候就会判断是否同源,如果不是同源的话,请求将会报错并无法正常处理response。
Q&A
在此,我们来一一解答下上面的疑问,找到答案的小伙伴这里可以略过啦;
Q1:为什么我这边几个开发的电脑都没有这种问题,但是产品却大概率有这问题?
A1:开发的时候会勾选disable cache
,此时浏览器将不会走本地缓存。产品的电脑一般都没有进行勾选,所以当切换域名时,请求的还都是从本地缓存中的资源,response header等其他数据也都是之前缓存过的,所以当使用资源使用crossorigin属性,就会报错。
Q2:为什么https://j1.cdn.com.cn/aa.js
的response headers
中的域名不是当前域名?
A2:综上,因为cdn缓存住了请求https://j1.cdn.com.cn/aa.js
的内容及response headers等
信息;
Q3:为什么同样是j1.cdn.com.cn
域名下两个文件(aa.js和bb.js)的access-control-allow-origin
却不一样?
A3:综上,因为这两个请求的资源是由不同的cdn返回的,所以缓存的内容也不一致,查看response header 可以看到,aa.js是x-via: DIANXIN-TIANJIN_41(200:miss);DIANXIN-TIANJIN_39(200:hit)
,bb.js是x-via: DIANXIN-TIANJIN_41(200:hit)
Q4:为什么浏览器的disable cache
项可以影响到后端返回的response headers
,以至于资源无法加载?
A4:并不是影响到了后端的返回,而是浏览器的机制。当查看到设置了crossorigin属性且请求返回值的access-control-allow-origin
与当前域名不一致后抛出了错误,不再进行进一步渲染等操作;
Q5:这个浏览器所谓的response headers
是由于资源请求有问题,浏览器自身机制直接取上次的值呢?
A5:这个上面有写到,使用curl或者抓包工具,可以看到后端返回的就是控制台network展示的这些信息。