对 CORS 的缓存效果: 请求的资源上没有“访问控制-允许-起源”头

这个问题的简短版本是,我们看到典型的 CORS 错误(x has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.) ,但是我们绝对发送指定的头。开始时请求是可以的,但是在 n (模式未确定)的时间之后,一些请求(除了 html 文件中引用的随机1或2个资产之外,没有真正的模式)会突然开始失败。在硬刷新或禁用缓存时,问题得到解决。

我们想知道缓存在这种情况下会如何影响 CORS? 或者问题是否在其他地方?

我们看到的是资产在第一个实例中加载得很好。

下面是浏览器发送到服务器(S3前面的云层)的内容的 cURL 表示(铬合金,未在其他地方测试) :

curl -I 'https://assets-frontend.kalohq.ink/style.allapps.add899080acbbeed5bb6a7301d234b65.css' -H 'Referer: https://lystable.kalohq.ink/projects/2180?edit=true' -H 'Origin: https://lystable.kalohq.ink' -H 'DPR: 2' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gec

这些标题看起来像是:

HTTP/1.1 200 OK
Content-Type: text/css
Content-Length: 5632
Connection: keep-alive
Date: Wed, 28 Jun 2017 09:23:04 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET
Access-Control-Max-Age: 3000
Last-Modified: Wed, 28 Jun 2017 09:16:15 GMT
ETag: "ece4babc2509d989254638493ff4c742"
Cache-Control: max-age=31556926
Content-Encoding: gzip
Accept-Ranges: bytes
Server: AmazonS3
Vary: Origin,Access-Control-Request-Headers,Access-Control-Request-Method
Age: 3384
X-Cache: Hit from cloudfront
Via: 1.1 adc13b6f5827d04caa2efba65479257c.cloudfront.net (CloudFront)
X-Amz-Cf-Id: PcC2qL04aC4DPtNuwCudckVNM3QGhz4jiDL10IDkjIBnCOK3hxoMoQ==

在此之后,你可以浏览网站一段时间,刷新几次,一切都很好。

但是,您可能会刷新,然后突然看到控制台中的错误:

Access to CSS stylesheet at 'https://assets-frontend.kalohq.ink/style.allapps.add899080acbbeed5bb6a7301d234b65.css' from origin 'https://kalohq.ink' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://kalohq.ink' is therefore not allowed access.

此时,如果您硬刷新或禁用缓存并重新加载页面,一切都将恢复正常工作。这就是为什么我们现在指向使用 CORS 的浏览器缓存行为。

载入这些资产的 HTML 文件如下:

<!doctype html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><title>Kalo</title><meta name="description" content="Kalo is used by the best teams on the planet to onboard, manage, and pay their freelancers. "><meta name="viewport" content="width=device-width,initial-scale=1"><meta http-equiv="Accept-CH" content="Width,DPR,Save-Data"><script>window.performance&&"function"==typeof window.performance.mark&&window.performance.mark("start load bootstrap"),console.log("Kalo v0.214.1 🎉")</script><script type="text/javascript" crossorigin="anonymous">window.webpackManifest={0:"moment-timezone-data.8189aab661847dea1b73.chunk.js",1:"1.7645e36f0742ed31139b.chunk.js",2:"2.bf0a1c9b400d715e3138.chunk.js",3:"3.d077b7a1cede6f6960e6.chunk.js",4:"4.0bbd51f182d8fa3f4951.chunk.js",5:"5.1dcf124ea7874546fc7a.chunk.js",6:"6.85ee04326ef5cfe2c084.chunk.js",7:"7.cf718eabaa3814fcb47c.chunk.js",8:"8.4c4c5b070e09afe037a1.chunk.js",9:"9.ba3b9a5f540f057fca46.chunk.js",10:"10.3c850061770df8801575.chunk.js",11:"11.df971dd9c4ab435fd421.chunk.js",12:"12.81905afa591a4796dcfc.chunk.js",13:"13.0f78c0c77d45cd79ac26.chunk.js",14:"14.f8f9f24d15e1cc4372a1.chunk.js",15:"15.6badd92530b5da668e98.chunk.js",16:"16.ef87b8dc2f87ca2d40a1.chunk.js",17:"17.bf842b852470057c4f0b.chunk.js",18:"18.f091321e6a0bbf16bf1f.chunk.js",19:"19.0297861a162b49308887.chunk.js",20:"20.7281da4b01eb4eb4bf1f.chunk.js",21:"21.781ca5137a9c76031df2.chunk.js",22:"22.c7dfd45fc0bd41c7618d.chunk.js",23:"23.8c4885794fd57453884a.chunk.js",24:"24.1447090b6f41a311414e.chunk.js",25:"25.021a38e680888fe2ac7e.chunk.js",26:"26.1afe06be0d6164d3409a.chunk.js",27:"27.dc70b696039ad4762a3b.chunk.js",28:"28.8c383709ce92ecae6b0c.chunk.js",29:"29.f594eb538f606ae17c50.chunk.js",30:"30.a2c1dfc70e0fac57b2a4.chunk.js",31:"31.2eaee95b85227b23ccd8.chunk.js",32:"32.528e99c8151fef966483.chunk.js",33:"33.c3b7530ab92bc1280136.chunk.js",34:"34.1eb5635dc498ad450839.chunk.js",35:"35.e71c1e7bc6092ff2a35f.chunk.js",36:"36.0d174c67ddb177944140.chunk.js",37:"37.af1c6ed4cde9120da636.chunk.js",38:"38.fb0dd22a16e7b597ef93.chunk.js",39:"39.c17f705a3438de3dc997.chunk.js",40:"40.d509fa240e2adf2888aa.chunk.js",41:"41.37d2f0e0e06a3c7d816b.chunk.js",42:"42.4febbf78adc3084afec3.chunk.js",43:"43.7aa48b320fcf69adb0a3.chunk.js",44:"44.5e6da9391c7412910447.chunk.js",45:"45.a17d5b7c5e534f260841.chunk.js",46:"46.a1d3a7790959ac892ed0.chunk.js",47:"47.241627b0e5da4ce35606.chunk.js",48:"48.84f9532a64f5a3beb20c.chunk.js",49:"49.f8527afe7cade8fc293a.chunk.js",50:"50.776b466f9019479de8fc.chunk.js",51:"51.ca34827c84d4bcc82079.chunk.js",52:"52.517f4f6c63395646cdd7.chunk.js",53:"53.e3a2103e4151cd13300f.chunk.js",54:"athena.5e6c5b01662cea2c8b1a.chunk.js",55:"hera.b69b80db056ad9c9389f.chunk.js",56:"hermes.29bb236b97c128e8b6ee.chunk.js",57:"iris.834233a6fb064bf576a9.chunk.js",58:"hephaestus.7ac71b3274dda739ba1f.chunk.js",59:"59.ce1aefa687f2ef9c9908.chunk.js",60:"60.5070b818882287dfc402.chunk.js",61:"61.19d5149d0a2bd9ef3c1e.chunk.js",62:"62.d7831f900b939591822e.chunk.js"}</script><link rel="shortcut icon" href="https://assets-frontend.kalohq.ink/favicon.ico" crossorigin="anonymous"><link href="https://assets-frontend.kalohq.ink/style.allapps.add899080acbbeed5bb6a7301d234b65.css" rel="stylesheet" crossorigin="anonymous"><link href="https://assets-frontend.kalohq.ink/style.hermes.689f9795642815d4b8afd20e446a174d.css" rel="stylesheet" crossorigin="anonymous"><link rel="preload" href="https://assets-frontend.kalohq.ink/hermes.29bb236b97c128e8b6ee.js" as="script" crossorigin="anonymous"><link rel="preload" href="https://assets-frontend.kalohq.ink/style.hermes.689f9795642815d4b8afd20e446a174d.css" as="style" crossorigin="anonymous"><link rel="preload" href="https://assets-frontend.kalohq.ink/allapps.commons.8395b1aa9666e3271c40.js" as="script" crossorigin="anonymous"><link rel="preload" href="https://assets-frontend.kalohq.ink/style.allapps.add899080acbbeed5bb6a7301
d234b65.css" as="style" crossorigin="anonymous"><link rel="preload" href="https://assets-frontend.kalohq.ink/vendor.83e606c69fc5ae7aeb9b.js" as="script" crossorigin="anonymous"><link rel="preload" href="https://assets-frontend.kalohq.ink/core/styles/fonts/Fakt-Soft-Pro-SemiBold/FaktSoftPro-SemiBold.1901bce5eea18c64a60693e961585ba1.woff" as="font" crossorigin="anonymous"><link rel="preload" href="https://assets-frontend.kalohq.ink/core/styles/fonts/Fakt-Soft-Pro-Blond/FaktSoftPro-Blond.4ab21e2be2f31a0ab8d798a9c65f99c1.woff" as="font" crossorigin="anonymous"><link rel="prefetch" href="https://assets-frontend.kalohq.ink/hera.b69b80db056ad9c9389f.js" crossorigin="anonymous"><link rel="prefetch" href="https://assets-frontend.kalohq.ink/iris.834233a6fb064bf576a9.js" crossorigin="anonymous"><link rel="prefetch" href="https://assets-frontend.kalohq.ink/athena.5e6c5b01662cea2c8b1a.js" crossorigin="anonymous"><link rel="prefetch" href="https://assets-frontend.kalohq.ink/moment-timezone-data.8189aab661847dea1b73.chunk.js" crossorigin="anonymous"><link rel="prefetch" href="https://assets-frontend.kalohq.ink/style.hera.f00a272db8e5756775fb2632e67c1056.css" crossorigin="anonymous"><link rel="prefetch" href="https://assets-frontend.kalohq.ink/style.iris.1465dc22f4279c748a04c66f3b4494de.css" crossorigin="anonymous"><link rel="prefetch" href="https://assets-frontend.kalohq.ink/style.athena.6acb14c0d060121364c9a0cf3e6fa0ad.css" crossorigin="anonymous"><link rel="prefetch" href="https://assets-frontend.kalohq.ink/_/node_modules/@kalo/ui/icon/fonts/MaterialIcons/MaterialIcons-Regular.012cf6a10129e2275d79d6adac7f3b02.woff" crossorigin="anonymous"><link rel="prefetch" href="https://assets-frontend.kalohq.ink/core/assets/fonts/MaterialIcons-Regular.012cf6a10129e2275d79d6adac7f3b02.woff" crossorigin="anonymous"><link rel="prefetch" href="https://assets-frontend.kalohq.ink/_/node_modules/@kalo/ui/icon/fonts/MaterialIcons/MaterialIcons-Regular.570eb83859dc23dd0eec423a49e147fe.woff2" crossorigin="anonymous"><link rel="prefetch" href="https://assets-frontend.kalohq.ink/core/assets/fonts/MaterialIcons-Regular.570eb83859dc23dd0eec423a49e147fe.woff2" crossorigin="anonymous"></head><body><main id="app"><!--[if lt IE 8]>
<p class="browserupgrade">You are using an outdated browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
<![endif]--><noscript>Kalo - Work without boundaries Please wait a moment as we load Kalo. Please make sure you have Javascript enabled to continue. Kalo’s aim is to give companies complete visibility over their external network.</noscript><noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-5XLW75" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript></main><div class="root __splash"><style>html{position:static!important;overflow-y:auto}.root{transition:opacity .35s linear;color:#234957;background-color:#f9fafc;position:absolute;top:0;right:0;bottom:0;left:0;opacity:1}.root.exit{opacity:0!important}.navigation{height:60px;background:#fff;border-bottom:1px solid #eceff1}.login{background:#ea5f6e;position:absolute;top:0;left:0;bottom:0;width:50%;display:flex;justify-content:center;align-items:center}@media screen and (max-width:767px){.login{width:100%;right:0}}.hide{display:none!important}.logo{height:107px}</style><div id="navbar" class="navigation hide"></div><div id="login" class="login hide"><div class="logo"><svg width="160" height="70" viewBox="0 0 206 90" xmlns="http://www.w3.org/2000/svg"><title>Kalo</title><path fill-rule="evenodd" fill="#fff" d="M17.629 47.172c2.31 0 4.254-.986 6.078-2.833l18.845-19.706c1.824-1.971 3.89-2.957 6.323-2.957 7.294 0 10.212 9.114 5.835 13.55L35.378 54.562l18.724 19.706c3.283 3.571 3.526 8.498.244 12.07-1.46 1.601-3.406 2.464-5.837 2.464-2.552 0-4.62-.986-6.2-2.834L23.707 65.646c-1.7-1.847-3.647-2.832-5.835-2.832h-1.58v17.612c0 4.804-3.405 8.5-8.147 8.5-4.376 0-8.145-3.942-8.145-8.5V8.498C0 3.695 3.647 0 8.145 0c4.5 0 8.147 3.695 8.147 8.498v38.674h1.337zm97.134 29.56c0 2.586-.972 4.433-2.916 5.789-6.566 4.557-15.077 6.773-25.654 6.773-16.656 0-25.653-9.236-25.653-21.676 0-11.455 8.146-20.076 25.045-20.076 3.891 0 8.39.616 13.496 1.848v-3.326c0-6.528-3.283-9.608-11.55-9.608-3.525 0-7.417.74-11.672 2.095-6.686 2.094-11.185-1.11-11.185-6.405 0-3.572 1.823-6.035 5.35-7.513 4.742-2.094 10.698-3.08 17.871-3.08 17.872 0 26.868 8.376 26.868 25.003v30.176zm-15.682-4.68V60.965c-4.378-1.354-8.39-1.97-12.159-1.97-6.443 0-10.577 3.202-10.577 8.006 0 5.296 4.134 8.252 10.942 8.252 4.5 0 8.51-1.11 11.794-3.203zm39.845 8.904c0 4.803-3.405 8.498-8.147 8.498-4.376 0-8.145-3.941-8.145-8.498V9.15c0-4.803 3.647-8.62 8.145-8.62 4.5 0 8.147 3.817 8.147 8.62v71.806zm57.513 1.359c-5.348 4.681-12.035 7.02-20.06 7.02-7.903 0-14.589-2.339-20.06-7.02-5.471-4.68-8.511-10.715-9.118-17.982-.365-5.788-.365-11.7 0-17.612.607-7.391 3.525-13.426 8.996-18.106 5.472-4.68 12.28-7.02 20.183-7.02 8.024 0 14.71 2.34 20.06 7.02 5.349 4.68 8.389 10.715 8.997 18.106.365 5.789.365 11.7 0 17.488-.608 7.391-3.648 13.427-8.998 18.106zm-7.172-33.009c-.363-7.02-5.229-11.946-12.887-11.946-7.417 0-12.402 4.68-13.01 11.946a69.483 69.483 0 0 0 0 12.318c.608 7.266 5.593 11.946 13.01 11.946 7.416 0 12.4-4.68 12.887-11.946a69.326 69.326 0 0 0 0-12.318z"/></svg></div></div><script>"/login"===window.location.pathname&&-1===document.cookie.indexOf("VIEW=")?document.getElementById("login").classList.remove("hide"):document.getElementById("navbar").classList.remove("hide"),document.querySelector(".__splash.root").id="splash"</script></div><script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Symbol,fetch,Intl.~locale.en&amp;unknown=polyfill"></script><script src="https://apis.google.com/js/client.js" async></script><script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDteWPK1-k97egIjYcX8-Btt8SpRsHit50&libraries=places" async></script><script>!function(e,t,a,n,c,o,s){e.GoogleAnalyticsObject=c,e[c]=e[c]||function(){(e[c].q=e[c].q||[]).push(arguments)},e[c].l=1*new Date,o=t.createElement(a),s=t.getElementsByTagName(a)[0],o.async=1,o.src="https://www.google-analytics.com/analytics.js",s.parentNode.insertBefore(o,s)}(window,document,"script",0,"ga"),ga("create","","auto")</
script><script>!function(e,t,a,n,g){e[n]=e[n]||[],e[n].push({"gtm.start":(new Date).getTime(),event:"gtm.js"});var m=t.getElementsByTagName(a)[0],r=t.createElement(a);r.async=!0,r.src="https://www.googletagmanager.com/gtm.js?id=GTM-5XLW75",m.parentNode.insertBefore(r,m)}(window,document,"script","dataLayer")</script><script>!function(){function t(){var t=a.createElement("script");t.type="text/javascript",t.async=!0,t.src="https://widget.intercom.io/widget/s21m3m5m";var e=a.getElementsByTagName("script")[0];e.parentNode.insertBefore(t,e)}var e=window,n=e.Intercom;if("function"==typeof n)n("reattach_activator"),n("update",intercomSettings);else{var a=document,c=function(){c.c(arguments)};c.q=[],c.c=function(t){c.q.push(t)},e.Intercom=c,e.attachEvent?e.attachEvent("onload",t):e.addEventListener("load",t,!1)}}()</script><script type="text/javascript" src="https://assets-frontend.kalohq.ink/vendor.83e606c69fc5ae7aeb9b.js" crossorigin="anonymous"></script><script type="text/javascript" src="https://assets-frontend.kalohq.ink/allapps.commons.8395b1aa9666e3271c40.js" crossorigin="anonymous"></script><script type="text/javascript" src="https://assets-frontend.kalohq.ink/hermes.29bb236b97c128e8b6ee.js" crossorigin="anonymous"></script></body></html>

这里需要注意的是,所有的 scriptlink标记都有 crossorigin="anonymous"

这个问题主要影响样式表,但是脚本似乎也以同样的方式受到了影响。同样,它似乎是随机选择哪些资产将在何时破产,这真的很奇怪。考虑到这两个事实,它甚至可能基于文档/装载顺序中的引用顺序。

一些最后的澄清希望能有所帮助:

  • 从 s3前面的 Cloudfront 提供的资产(请参见响应头)
  • 除了 Chrome 之外,目前还没有其他浏览器的报告/测试,不过希望很快就能更新
  • 所有脚本和样式表资产都使用

任何有关这个问题的帮助或指导都将受到极大的赞赏。目前这个问题相当阻塞!

更新:

因此,我们设法得到了什么似乎是一个持续的工作建设没有任何明显的问题。没有时间,很难100% 确定,因为问题看起来是零星的/随机的。我们改变了以下内容:

  • 绕过 Cloudfront 直接引用 S3中的资产?
  • access-control-max-age设置为 -1,禁用此选项。我们不希望这样做有任何效果,因为这只会(读取规范)影响在 GET 请求中不会发生的飞行前请求。
  • 删除预加载/预取链接标记。

我们现在正在做进一步的测试,试图将其中的一个或一组分离出来作为罪魁祸首。然后我们可以进一步挖掘那里正在发生的事情。

注意这个 解决问题问题现在已被证明是不正确的。参见 更新2

更新2:

在我们认为绕过了这一问题的上一次推出之后,我们收到了关于这一问题的进一步报告和内部事件。之前的推出确实产生了一个影响,那就是这个问题现在已经不那么常见了。再一次,一个硬刷新修复一切。

这个问题与前面描述的仍然相同,到目前为止,我们还没有看到第一次出现加载 JS 失败的第一手资料-现在看来总是一个 CSS 文件失败。

更新3:

最初我没有提到的一些非常重要的信息是在这个问题开始出现的时候发生的变化。

上周一,我们发布了一个由 网络包驱动的捆绑式重构,这意味着部署之间可以共享资产。例如,如果输出文件 allapps.commons.HASH123.css在版本1和版本2之间没有变化,那么我们可以利用浏览器缓存。

然而,仍然会发生的情况是,将这些资产上传到 S3是的的脚本目前正在静默地上传和覆盖原始文件。我们假设这种更改是相当无害的,因为文件的名称和内容是相同的,但也许这有一些不利影响?

这个版本的另一个影响是,现在将有更多的资产,由于积极的 代码分裂。但是这里需要注意的一点是,没有一个异步块似乎遇到同样的问题(它们毕竟使用了 jsonp) ,问题只是通过 <script><link>标记引用了这些资产。

您可以找到在中断版本 给你之前的版本的构建工件。并找到当前活动版本的新构建工件,显示罕见的问题 给你。您还可以找到我们的部署脚本 给你

所有资源可以在谷歌驱动器 给你上找到。

更新4:

这个问题仍在发生,现在已经在按需加载的异步块上进行了报告。查看 webpack 运行时,这些脚本是通过向页面添加一个新的 script 标记加载的,同样使用 crossorigin="anonymous"

更新5:

在每个构建中,我们现在在散列文件名时使用一个惟一的 salt (发布版本)。这意味着构建之间不共享任何资产。这个问题在本版本发布后仍然存在。

更新6:

I've uploaded a .har文件 showing this issue occurring over a user session.

搜索以下字符串 "url": "https://assets-frontend.kalohq.ink/style.allapps.add899080acbbeed5bb6a7301d234b65.css",并查看对此资产的各种请求。你会看到前几个是好的,并有你所期望的标题。最后一个匹配项(第32624行)是失败的匹配项。

{
"startedDateTime": "2017-06-28T09:40:15.534Z",
"time": 0,
"request": {
"method": "GET",
"url": "https://assets-frontend.kalohq.ink/style.allapps.add899080acbbeed5bb6a7301d234b65.css",
"httpVersion": "unknown",
"headers": [
{
"name": "Referer",
"value": "https://kalohq.ink/account"
},
{
"name": "Origin",
"value": "https://kalohq.ink"
},
{
"name": "DPR",
"value": "2"
},
{
"name": "User-Agent",
"value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
}
],
"queryString": [],
"cookies": [],
"headersSize": -1,
"bodySize": 0
},
"response": {
"status": 0,
"statusText": "",
"httpVersion": "unknown",
"headers": [],
"cookies": [],
"content": {
"size": 0,
"mimeType": "x-unknown"
},
"redirectURL": "",
"headersSize": -1,
"bodySize": -1,
"_transferSize": 0,
"_error": ""
},
"cache": {},
"timings": {
"blocked": -1,
"dns": -1,
"connect": -1,
"send": 0,
"wait": 0,
"receive": 0,
"ssl": -1
},
"serverIPAddress": "",
"pageref": "page_10"
},

更新7:

因此,昨晚我们推出了一个更改,删除了 crossorigin="anonymous"属性 无处不在的使用。到目前为止,我们还没有看到问题发生(考虑到问题的性质,仍然在等待) ,但是现在看到了一些来自请求的有趣和意想不到的响应。如果我们能弄清楚这里到底发生了什么就太好了。我不相信我们期望删除 crossorigin="anonymous"有这样的影响,甚至理解为什么它是如此破碎之前,因为我们的服务器设置为发送正确的头和 Vary头。

请求从 cli 到 s3,有一个起源标头,没有 cors 响应标头

curl -I 'https://s3.amazonaws.com/olympus.lystable.com/style.allapps.5ebcc4d28ec238a53f46d6c8e12900d1.css' -H 'Pragma: no-cache' -H 'Accept-Encoding: gzip, deflate, br' -H 'Accept-Language: en-GB,en-US;q=0.8,en;q=0.6' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/59.0.3071.109 Chrome/59.0.3071.109 Safari/537.36' -H 'Accept: text/css,*/*;q=0.1' -H 'Referer: https://asos.kalohq.com/categories' -H 'Connection: keep-alive' -H 'DPR: 1' -H 'Cache-Control: no-cache' -H "Origin: https://kalohq.com" --compressed
HTTP/1.1 200 OK
x-amz-id-2: kxOvBrYsKyZ42wGgJu8iyRZ8q6j5DHDC6QoK1xn2e8FO1wIEEVkxQ0JvGQTmwrN/Njf8EOlmLrE=
x-amz-request-id: DA8B5488D3A7EF73
Date: Thu, 13 Jul 2017 13:27:47 GMT
Last-Modified: Thu, 13 Jul 2017 11:30:50 GMT
ETag: "c765a0a215cb4c9a074f22c3863c1223"
Cache-Control: max-age=31556926
Content-Encoding: gzip
Accept-Ranges: bytes
Content-Type: text/css
Content-Length: 5887
Server: AmazonS3

稍后请求再次从 cli 到 s3,只有原点头。现在突然给所有预期的 Cors 回头..。

curl -H "Origin: https://kalohq.com" -I https://assets-frontend.kalohq.com/style.allapps.5ebcc4d28ec238a53f46d6c8e12900d1.css
HTTP/1.1 200 OK
Content-Type: text/css
Content-Length: 5887
Connection: keep-alive
Date: Thu, 13 Jul 2017 13:33:09 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET
Access-Control-Max-Age: -1
Last-Modified: Thu, 13 Jul 2017 11:30:50 GMT
ETag: "c765a0a215cb4c9a074f22c3863c1223"
Cache-Control: max-age=31556926
Content-Encoding: gzip
Accept-Ranges: bytes
Server: AmazonS3
Vary: Origin,Access-Control-Request-Headers,Access-Control-Request-Method
Age: 69
X-Cache: Hit from cloudfront
Via: 1.1 a19c66da9b402e0bee3fd29619661850.cloudfront.net (CloudFront)
X-Amz-Cf-Id: 3wQ7Z6EaAcMscGirwsYVi1M_rvoc1fbI034QY4QZd6IqmlRzLRllEg==

更新8:

删除 crossorigin="anonymous"标记解决了这个问题。因为我们在脚本标记 之前上有这个属性,所以关于为什么这个版本突然开始出现问题的调查正在进行中。


在这次调查中所有有用的资源可以在谷歌驱动器 给你上找到。

31826 次浏览

https://assets-frontend.kalohq.ink/style.allapps.add899080acbbeed5bb6a7301d234b65.css only returns CORS headers when an "Origin" header is present (which is sent with a CORS request, but not regular requests).

Here's what happens:

  1. User fetches CSS as part of a no-CORS request (eg, <link rel="stylesheet">). This caches due to the Cache-Control header.
  2. User fetches CSS as part of a CORS request. The response comes from the cache.
  3. CORS check fails, no Access-Control-Allow-Origin header.

The server is at fault here, it should use the Vary header to indicate its response changes depending on the Origin header (and others). It sends this header in response to CORS requests, but it should send it in response to non-CORS requests too.

Chrome is somewhat at fault here, as it should use the credentials mode of the request as part of the caching key, so a non-credentialed request (such as those sent by fetch()) shouldn't match items in the cache that were requested with credentials. I think there are other browsers that behave like Chrome here, but Firefox doesn't.

However, since you're using a CDN, you can't rely on browsers to get this right, as the caching may still happen at the CDN. Adding the correct Vary header is the right fix.

tl;dr: Add the following header to all of your responses that support CORS:

Vary: Origin, Access-Control-Request-Headers, Access-Control-Request-Method

I can shed a little light to how it happened with us. Azure CDN (which we use) does not support Vary: headers right now. So far so bad. But now we use the script crossorigin attribute which - and thats the interesting thing - is not supported by some browsers.

If now such browser comes to our site, it does not send origin: because it does not understand the "crossorigin" attribute. If later another one comes who understands it, it will send origin: -> CORS Error because first response is cached.

Ugly.

We experienced the same problem when migrating our JS to Webpack.

Our setup is similar:

  • assets are uploaded to an S3 bucket during deployment
  • the bucket is set as the Cloudfront origin

When migrating to Webpack, we wanted to take advantage of JS sourcemaps for better error reporting to Airbrake. To allow errors to be catched properly, the crossorigin="anonymous" attribute had to be set on the script tags. The reason why is explained here: https://blog.sentry.io/2016/05/17/what-is-script-error.html

Part of the problem was that CORS response headers were sometimes returned, sometimes not, triggering a CORS errror in the browser. Cloudfront servers were caching the response with OR without the CORS headers, depending on the first client request making a Miss request.

So two possible outcomes:

  1. First client does not send the Origin request header => Cloudfront server caches the response without CORS headers.
  2. First client sends the Origin request header => Cloudfront server caches the response without CORS headers.

This made the problem look like it was random, but it was just a matter of race condition (how the first client made the request) and different headers cached on different Cloudfront servers: timing and location dependent. Add to that the fact that browsers might cache these wrong headers...

So you need to properly configure Cloudront's distribution behavior to:

  • allow and cache preflight (OPTIONS) requests
  • base the cache on CORS request headers (Origin, Access-Control-Request-Headers, Access-Control-Request-Method)

Here is the configuration that solved our problem.

S3 bucket / Permissions / CORS configuration:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>HEAD</AllowedMethod>
<MaxAgeSeconds>300</MaxAgeSeconds>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

Cloudfront distribution / Edit Behavior:

enter image description here


We now experience a problem similar to yours, as we just migrated our CSS to Webpack. We are experiencing even more sporadic CORS errors for CSS files. We are trying to remove the crossorigin="anonymous" attribute on <link rel="stylesheet" /> tags since we don't need error tracking for CSS files.

I want to share that we were having the same issue, but in this case, specifically preloading some fonts. We noticed that the combination of S3, CloudFront, and Safari was killing us, so we decided to remove preload and crossorigin="anonymous".

We were trying to do this:

<link rel="preload" href="<zzz.cloudfrontUrl.com>" as="font" crossOrigin="anonymous" />

But safari somehow corrupted the cache and it was giving not allowed access-control-allow-origin but only sometimes.

Our assumption there is that maybe there is a problem between safari and preload fonts from maybe any CDN and the crossOrigin="anonymous" (which is required).

Regards.

Assuming that the CROS configuration has been set in the S3 already, these two points below can make sure that the video will be always loaded on the browser.

  1. Add crossorigin=”anonymous” on the tag
  2. Add the "?q=#{Time.now.to_i}" at the end of the S3 URLs

HAML + Ruby code will look something like this.

    %video{controls: "", controlslist: "nodownload", crossorigin: 'anonymous'}
%source{src: "#{s3_url}?q=#{Time.now.to_i}", type: "video/mp4"}

I was having same problem here is how I solved it. Added Corsrule for wildcard domain(you can choose you origin domain)

<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>HEAD</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>*</AllowedHeader>
</CORSRule>

Then go to

CloudFront Distribution > Origin and Origin Groups > Edit Origin

In your "Origin Custom Headers" fill "Header Name" value "origin" and "Value" to "https://www.yourorigindomain.com"

When you click on info icon in the right of Origin Custom Headers you will see the message:

All custom header keys and values you specify here will be included in every >request to this origin. If a header was already supplied in the client request, it is overridden.

So cloudfront add origin header in every request it made to s3 bucket, wheather you pass it from client or not and cache response the headers.

You can check access-control-allow-origin: * in response header using

`curl -i https://cloufrontdistributiondomain.com/example.png`

i.e without passing the host.

When using the Azure CDN, Vary headers are ignored for the cached content, which means Azure only keeps one copy per value of the Accept-Encoding header. This of course introduces issues with CORS.

In order to avoid such issues, the workaround can be to rely on the rules engine and to force the headers:

  • Global rule:
    • Overwrite Vary response header with value: Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Accept-Encoding
    • Overwrite Access-Control-Allow-Origin response header with value: https://<your domain>
    • Delete Server response header
  • Create additional rules for each potential Origin:
    • If Request Header Origin Equals ...
    • The response headers should be defined in a similar way to the global rule

I ran into a similar situation. And I want to document my debugging process to help more people.

We are building an online image editor. Because we need to export canvas as an image, we need to handle cors for images properly (with crossOrigin: "anonymous").

Similar to the PO, our images are hosted on s3 with cloudfront as the CDN. I noticed the same cors error as that of the question.

Related Details: both the s3 bucket and the cloudfront CDN had been running for a while without the headers:

access-control-allow-origin: *

access-control-allow-methods: PUT, POST, DELETE, GET

The headers were only added recently by following: https://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html https://aws.amazon.com/premiumsupport/knowledge-center/no-access-control-allow-origin-error/

The issue was very strange to me, because I was pretty sure I set s3 and cloudfront properly to return the headers. I could confirm it with curl, by passing an "Origin".

So my initial guess was that this was a chrome bug.

My friends and I tried different environments, the issue can't be reproduced consistently. Here is the summary:

  • Ubuntu 1 + chrome : repro
  • Ubuntu 1 + firefox : no repro
  • Mac 1 + chrome : repro
  • Mac 1 + safari : repro
  • Mac 2 + chrome : no repro
  • Mac 3 + chrome : no repro

Our chrome version is the same 87.0.4280.88 (Official Build) (64-bit)

Another strange thing was that the issue would be gone randomly while I was trying to debug the issue for no reason. However, the issue would come back next day.

I suspected browser cache at first. I tried clearing cache / hard reload, incognito mode, and a completely fresh chrome session, using:

google-chrome --user-data-dir=~/chromeTemp

They couldn't resolve the issue.

So the key question to ask was if this is an AWS issue or this is a chrome issue.

Although chrome's devtool did show that the headers didn't present,

access-control-allow-origin: *

access-control-allow-methods: PUT, POST, DELETE, GET

given the strangeness of this issue and the opposite fact the curl command had told me, I couldn't trust chrome's devtool.

Plus, I also tried right clicking in chrome devtool's network panel over the failed image and choosing "copy as curl", and pasting that curl command line directly into a terminal, and the issue wasn't there either. The headers were simply returned.

Because the image is served by cloudfront with https, I didn't know how to intercept https traffic using wire shark. But I found chrome's network dump function:

chrome://net-export/

Later, I noticed that cloudfront has an option to serve using http. That could be an easier way to debug this issue.

And was able to capture the raw traffic:

enter image description here

Now it is very clear that AWS didn't return the right headers.

I tried immediately with curl with the "Origin" header:

curl -v -H "origin: http://localhost:9003" https://xxxxx.cloudfront.net/xxxxx.jpg

The headers were returned.

enter image description here

Notice that in addition to the "Origin" header, chrome sends a lot more headers to the cloudfront endpoint. So I asked if I can reproduce the issue with curl by sending exactly the same headers:

curl -v -H "pragma: no-cache" -H "cache-control: no-cache" -H "sec-ch-ua: "Google Chrome";v="87", " Not;A Brand";v="99", "Chromium";v="87"" -H "origin: http://localhost:9003" -H "sec-ch-ua-mobile: ?0" -H "user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36" -H "accept: image/avif,image/webp,image/apng,image/,/*;q=0.8" -H "sec-fetch-site: cross-site" -H "sec-fetch-mode: cors" -H "sec-fetch-dest: image" -H "referer: http://localhost:9003/" -H "accept-encoding: gzip, deflate, br" -H "accept-language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7,zh-TW;q=0.6" https://xxx.cloudfront.net/xxx/xxx.jpg

enter image description here

And indeed, if I serve exactly the same headers to cloudfront, I can reproduce the issue with curl too. Most importantly, notice that, when the issue reproduces, there is a x-cache header says "cache hit", whereas when it doesn't reproduce, the same header carries "cache miss".

So, what if I clear the cloudfront cache? I actually tried this first time I saw the bug. I did it by invalidating only the path of the problematic image. But this time, I wanted to invalidate the entire cloudfront cache for that s3 bucket.

After that, I tried again with the same curl command line, and it worked!

enter image description here

Notice that not only the headers were returned, it also said "cache miss".

So here is my conclusion / learning:

  1. Looks like cloudfront does caching not only based on the content itself, but also based on request headers. Different headers will result in different caching behavior and thus different returned data.
  2. The reproducible chrome might send different headers than those browsers that didn't reproduce the issue. Essentially they were reading from different caches.
  3. The request headers as shown and copied by the chrome devtool is not trust worthy.
  4. chrome://net-export/ is an awesome tool.

At this point, I'm 95% sure this is due to cloudfront caching behavior / stale data / headers. But given the intermittency of the issue, I will need to keep observing for a while to be sure.

Update 1:

Unfortunately, I checked the next day, the issue just came back:

enter image description here

I feel that this is cloudfront's bug.

I manage to solve the issue with the same setup using CloudFront + S3 serving files from another domain (CORS required).

For me, the CORS issue occurred for the most recent added SVG for unknown reasons (we have multiple SVG's used by CSS, which works fine). Our app is build using Webpack + Module federation which means the app shell handles the injection of CSS.

I used the chrome://net-export/ as mentioned above (GREAT tool, thanks!) to investigate the headers returned by CF/S3 and the access-control-allow-origin header was not included in the response for this specific file. The CORS issue now makes sense but why is CF/S3 not returning the CORS headers in the response?

Except adding CORS settings to S3 we added the Origin header to CloudFront under Behaviours/Cache key and origin requests/Legacy cache settings/headers where you can include custom headers.

Suppose the server sends a response with an Access-Control-Allow-Origin value with an explicit origin (rather than the "*" wildcard). In that case, the response should also include a Vary response header with the value Origin — to indicate to browsers that server responses can differ based on the value of the Origin request header. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin#cors_and_caching

As I understand from above the CF setting should not be required when using the Access-Control-Allow-Origin: * header. Anyway, now the CORS headers are included in the response together with the Vary: Origin header and it works.

Question still exist why this issue was introduced to this specific file...

Five years later I stumbled over this Post having a JavaScript image loading issue with CORS on S3 on Safari. The solution, in my case, was inbelievable:

This code works:

image.crossOrigin = 'anonymous';
image.src = resultSrc;

This code does not:

image.src = resultSrc;
image.crossOrigin = 'anonymous';

In fact, safari loads the picture on the first line, without CORS headers, and puts in into the cache. The he loads the image AGAIN, on the second line, with CORS headers, and correctly uses it (on the first request). ON second request, result comes from cache and fails.