什么可以解释浏览器间歇性地不加载某些 CORS(跨域)javascript 文件?

Posted

技术标签:

【中文标题】什么可以解释浏览器间歇性地不加载某些 CORS(跨域)javascript 文件?【英文标题】:What could explain the browser intermittently not loading some CORS (crossorigin) javascript files? 【发布时间】:2014-02-01 23:55:39 【问题描述】:

我最近向某些脚本标签添加了 crossorigin 属性,以使我的脚本能够从来自不同子域的脚本中收集错误信息。我在 nginx 中添加了 header 以允许跨域请求。

从那时起,我偶尔会在没有浏览器从服务器请求某些 javascript 文件的情况下加载页面。重新加载页面解决了这个问题,但这种情况已经发生了好几次了。

我认为这与 CORS 有关,但由于它大部分时间都有效,我不知道如何重现它。

以下是我的代码摘录:

Nginx 配置包含针对某个位置的 javascript 文件的这条规则:

add_header 'Access-Control-Allow-Origin' "$http_origin";

我在php页面中的脚本标签:

<script type="text/javascript" src="<?php print "$host/js/$filename?v=$version";?>" crossorigin></script>

当我的页面加载时,我可以在 Firebug 中看到脚本标签是正确的,但在 Firebug Net 选项卡中甚至没有显示请求。通常会显示一个请求,即使它使用文件的缓存版本。

我在这里发现了一个我认为可能相关的问题: all of my browsers are not sending the origin header 但进一步考虑,我认为应该有一个初始请求会失败并且会在 Firebug 中显示。

有人对为什么会发生这种情况有任何理论吗?

编辑: 我为我的 CORS 请求添加了单独的访问日志。 Nginx 添加 CORS 标头的位置中的任何文件也会记录在新的访问日志中。

当页面无法加载 javascript 文件时,Nginx 会记录一个 304 状态(未修改)的请求。这意味着 Nginx 仅在修改后才获得对资源的条件请求,并且 Nginx 发送回 304 而没有其他任何内容。如果缓存的版本没有加载 CORS 标头,则可能无法执行 javascript。

我有一个版本号,我将其附加到脚本标记的 src 中,如上所示。此版本号已更改。那么这不应该导致浏览器重新请求所有这些资源,就好像它们具有不同的文件名一样?我认为浏览器应该将这些视为未缓存的资源。不应该有任何可能导致 304 的请求。这可能是什么原因造成的?

【问题讨论】:

也许资源失败并出现一些错误,导致无法发送 cors 标头?服务器错误日志中有任何内容吗? 服务器日志中没有错误。它只是一个静态的 javascript 文件,很少会导致服务器端失败。 我得到了更多的日志信息并编辑了问题。 很奇怪。但是:That means that Nginx got a conditional request for the resource only if modified, and Nginx sent back the 304 and nothing else. 在我看来,这是问题的根源,需要以某种方式进行修改? 我也是这么想的。但是每当我更改该版本号时,它不应该再检查修改了,因为每个资源都有不同的 url。这些 javascript 文件设置为“expires 30d;”在我的 Nginx 配置中。但是浏览器应该将它作为一个新的 url 来请求,而不是检查缓存的副本。 【参考方案1】:

我知道是什么原因造成的。首先,我们需要有关配置的更多信息。

我有 3 个主机,a.my.com、b.my.com 和 c.my.com。静态文件均来自 c.my.com,html 和 php 均来自 a.my.com 和 b.my.com。

我将 crossorigin 属性添加到我的脚本标签中,这样我就可以从我的脚本中获取有用的错误信息。请参阅此处描述此问题的 SO 问题:Cryptic "Script Error." reported in Javascript in Chrome and Firefox

我使用请求主机作为允许的来源而不是 *,作为将允许的来源列入白名单的前兆。有关更多信息,请参阅此问题:How to properly setup nginx Access-Control-Allow-Origin into response header based on the Origin header from the request?

我的 nginx 配置对静态内容的有效期很长,因此内容被缓存,不会因重复请求而加载我的服务器。我将版本号附加到静态内容的 url 中,以便在发生更改时重新请求它。版本号来自我在svn中的版本,所以当我将服务器文件更新到最新版本时,浏览器都会开始为每个静态文件加载一个新的url。

这就是正在发生的事情:

新用户访问 a.my.com 并加载页面。 Javascript 文件 1 到 5 从 c.my.com 加载以在 a.my.com 上使用。 Nginx 向它们发送一个原始标头,允许“a.my.com”和一个遥远的未来到期日期。此页面运行良好,javascript 加载成功。

然后,用户进入 b.my.com 站点的另一部分并加载页面。从 c.my.com 请求 Javascript 文件 1 到 10。文件 1 到 5 与为 a.my.com 加载的文件相同。浏览器知道它的缓存中有这些文件,所以它会向 nginx 询问它们的状态。 Nginx 回复 304(未修改),浏览器继续请求下一个文件。

所以现在浏览器已经从 c.my.com 加载了文件 6 到 10,并在缓存中查找了文件 1 到 5。缓存的文件附加了它们的 allow origin 标头,上面写着“a.my. com" 是允许的来源。请求文件的页面在 b.my.com 上,因此它不匹配并且不允许使用它们。所以它会默默地丢弃它们并向用户显示一个缺少 javascript 的页面。

解决方案:

一种解决方案是发送值为 * 的 Access-Control-Allow-Origin 以允许任何来源。这意味着即使它们没有启动第一次下载,当前源也允许缓存文件。正如这个 SO 问题中所讨论的那样,这有安全隐患:When is it safe to enable CORS?

第二种解决方案是让每个源对同一个静态文件使用不同的 url。这迫使浏览器单独缓存它们。这允许 Access-Control-Allow-Origin 标头特定于单个来源。代价是更多的资源请求来到服务器,用户的浏览器使用更多的内存和存储来缓存资源。

【讨论】:

优秀的答案。感谢您花时间写下您的发现。【参考方案2】:

您的响应标头中需要 Vary:Origin。

【讨论】:

以上是关于什么可以解释浏览器间歇性地不加载某些 CORS(跨域)javascript 文件?的主要内容,如果未能解决你的问题,请参考以下文章

浏览器画布 CORS 支持跨域加载图像操作

ajax跨域之CORS

跨域和CORS

Amazon S3 CORS 错误

UITextFields 间歇性地不允许用户交互

为啥一些跨域 XMLHttpRequest 请求不返回 CORS 错误