XMLHttpRequest 无法加载 XXX 没有“Access-Control-Allow-Origin”标头

XMLHttpRequest cannot load XXX No 'Access-Control-Allow-Origin' header

提问人:Peter David Carter 提问时间:2/22/2016 最后编辑:georgeawgPeter David Carter 更新时间:6/5/2023 访问量:384182

问:

TL的;博士;关于同源策略

我有一个 Grunt 进程,它启动 express.js 服务器的实例。直到现在它开始提供空白页面时,它一直工作正常,以下内容出现在 Chrome(最新版本)开发人员控制台的错误日志中:

XMLHttpRequest 无法加载 https://www.example.com/ 请求的 资源。因此,不允许访问源“http://localhost:4300”。

是什么阻止了我访问该页面?

JavaScript cors 同源策略

评论

0赞 Peter David Carter 2/22/2016
我正在网站上工作,五分钟前还好。
1赞 Jaromanda X 2/22/2016
它是否发出 CORS 标头?也许如果你分享了一些代码,它会更容易看到
0赞 Peter David Carter 2/22/2016
似乎。我应该问哪个部门了解?我只是主要做 backbone.marionette 的东西......
0赞 Peter David Carter 2/22/2016
是的。我想无论如何,部门的组织并不总是统一的,所以这可能是一个模糊的问题,但我想了解我公司的一些后端/路由/系统管理员的东西,这似乎是一个很好的借口来熟悉自己,所以如果将来有问题,我可以提供帮助。
0赞 Larry Lane 2/22/2016
我会问你操作内部服务器端的某个人。如果您之前能够访问它,他们一定已经在您身上更改了它。

答:

355赞 Quentin 2/22/2016 #1

tl;dr — 当您想从不同的服务器读取数据时,(主要)使用客户端 JS,您需要具有数据的服务器向需要数据的代码授予显式权限。

答案末尾有摘要,答案中有标题,以便于找到相关部分。不过,建议阅读所有内容,因为它为理解原因提供了有用的背景知识,这使得了解如何在不同情况下更容易应用

关于同源策略

这是同源策略。它是浏览器实现的安全功能。

您的具体情况是显示它是如何为 XMLHttpRequest 实现的(如果您使用 fetch,您将获得相同的结果),但它也适用于其他事情(例如加载到 a 上的图像或加载到 a 中的文档),只是实现略有不同。<canvas><iframe>

演示 SOP 需求的标准方案可以用三个字符来演示:

  • Alice 是一个拥有网络浏览器的人
  • Bob 经营一个网站(在您的示例中)https://www.example.com/
  • Mallory 运行一个网站(在您的示例中)http://localhost:4300

Alice 登录到 Bob 的网站,并在其中拥有一些机密数据。也许是公司内部网(只能通过局域网上的浏览器访问),或者是她的网上银行(只能通过输入用户名和密码后获得的cookie访问)。

Alice 访问了 Mallory 的网站,该网站包含一些 JavaScript,导致 Alice 的浏览器向 Bob 的网站发出 HTTP 请求(从她的 IP 地址和她的 cookie 等)。这可以像使用和读取 .XMLHttpRequestresponseText

浏览器的同源策略阻止 JavaScript 读取 Bob 网站返回的数据(Bob 和 Alice 不希望 Mallory 访问该网站)。(请注意,例如,您可以使用跨源的元素显示图像,因为图像的内容不会暴露给 JavaScript(或 Mallory)......除非您将 canvas 放入混合中,在这种情况下,您将生成同源违规错误)。<img>


为什么在您认为不应该适用同源策略时应用同源策略

对于任何给定的 URL,可能不需要 SOP。出现这种情况的几种常见情况是:

  • 爱丽丝、鲍勃和马洛里是同一个人。
  • Bob 提供的是完全公开的信息

...但是浏览器无法知道上述任何一项是否属实,因此信任不是自动的,而是应用了 SOP。在浏览器将从 Bob 收到的数据提供给其他网站之前,必须明确授予权限。


为什么同源策略适用于网页中的 JavaScript,但其他方面却很少

网页外

浏览器扩展、浏览器开发人员工具中的“网络”选项卡以及 Postman 等应用程序都是已安装的软件。他们不会仅仅因为您访问了另一个网站而将数据从一个网站传递到属于另一个网站的 JavaScript。安装软件通常需要更深思熟虑的选择。*

没有第三方(马洛里)被认为是有风险的。

*浏览器扩展确实需要仔细编写,以避免跨域问题。例如,请参阅 Chrome 文档

网页内部

大多数时候,当只是在网页上显示某些内容时,不会有大量的信息泄露。

如果您使用元素加载图像,则该图像会显示在页面上,但向 Mallory 公开的信息很少。JavaScript 无法读取图像(除非您使用属性显式启用 CORS 请求权限),然后将其复制到她的服务器。<img>crossOrigin

也就是说,一些信息确实泄露了,引用Domenic Denicola(谷歌)的话:

Web 的基本安全模型是同源策略。我们 在该安全性之前,该规则具有多个旧例外 模型已经到位,脚本标签是最令人震惊的标签之一 也是最危险的。(请参阅各种“JSONP”攻击。

许多年前,也许随着 XHR 或 Web 字体的引入(我 记不清了),我们在沙子上画了一条线,说没有新的 Web 平台功能会破坏同源策略。现有的 功能需要祖父级并经过仔细打磨 以及经常被利用的例外情况,为了不破坏网络, 但是,我们当然不能再在我们的安全策略中增加任何漏洞。

这就是为什么您需要 CORS 权限才能跨源加载字体的原因。


为什么你可以在不用JS读取的情况下在页面上显示数据

在许多情况下,Mallory 的网站可能会导致浏览器从第三方获取数据并显示它(例如,通过添加元素来显示图像)。不过,Mallory 的 JavaScript 不可能读取该资源中的数据,只有 Alice 的浏览器和 Bob 的服务器可以做到这一点,所以它仍然是安全的。<img>


CORS

错误消息中引用的 HTTP 响应标头是 CORS 标准的一部分,它允许 Bob 明确授予 Mallory 站点通过 Alice 的浏览器访问数据的权限。Access-Control-Allow-Origin

基本实现仅包括:

Access-Control-Allow-Origin: *

...以允许任何网站读取数据。

Access-Control-Allow-Origin: http://example.com

...将只允许特定站点访问它,并且 Bob 可以根据请求标头动态生成它,以允许多个(但不是所有)站点访问它。Origin

Bob 如何设置响应标头的细节取决于 Bob 的 HTTP 服务器和/或服务器端编程语言。Node.js/Express.js的用户应使用有据可查的CORS中间件。其他平台的用户应该查看此指南集合,了解可能会有所帮助的各种常见配置

Model of where CORS rules are applied

注意:有些请求很复杂,会发送一个预检 OPTIONS 请求,服务器必须响应该请求,然后浏览器才会发送 JS 想要发出的 GET/POST/PUT/Whatever 请求。仅添加到特定 URL 的 CORS 实现通常会因此而绊倒。Access-Control-Allow-Origin


显然,只有通过 CORS 授予权限是 Bob 只有在以下任一情况下才会做的事情:

  • 数据不是私有的,或者
  • 马洛里值得信赖

如何添加这些标头?

这取决于您的服务器端环境。

如果可以,请使用旨在处理 CORS 的库,因为它们将为您提供简单的选项,而不必手动处理所有内容。

Enable-Cors.org 提供了特定平台和框架的文档列表,您可能会发现这些文档很有用。

但我不是鲍勃!

Mallory 没有添加此标头的标准机制,因为它必须来自 Bob 的网站,而 Bob 无法控制。

如果 Bob 运行的是公共 API,那么可能有一种机制可以打开 CORS(可能通过以某种方式格式化请求,或者在登录到 Bob 站点的 Developer Portal 站点后使用配置选项)。不过,这必须是 Bob 实现的机制。Mallory 可以阅读 Bob 网站上的文档,看看是否有可用的内容,或者她可以与 Bob 交谈并要求他实现 CORS。


提及“印前检查响应”的错误消息

某些跨域请求是预检的。

当(粗略地说)您尝试发出以下跨域请求时,会发生这种情况:

  • 包括 Cookie 等凭据
  • 无法使用常规 HTML 表单生成(例如,设置无法在表单或其他请求标头中使用的 Content-Type)。enctype

如果您正确地执行了需要预检的操作

在这些情况下,此答案的其余部分仍然适用,但您还需要确保服务器可以侦听预检请求(这将是(而不是 、 或您尝试发送的任何内容)并使用正确的标头响应它,但也允许您的特定 HTTP 方法或标头。OPTIONSGETPOSTAccess-Control-Allow-OriginAccess-Control-Allow-MethodsAccess-Control-Allow-Headers

如果您错误地触发了印前检查

有时人们在尝试构造 Ajax 请求时会犯错误,有时这些错误会触发对预检的需求。如果 API 旨在允许跨域请求,但不需要任何需要预检的内容,则这可能会中断访问。

触发此问题的常见错误包括:

  • 尝试在请求上放置其他 CORS 响应标头。这些不属于请求,不做任何有用的事情(权限系统的意义何在,你可以授予自己权限?),并且必须只出现在响应中。Access-Control-Allow-Origin
  • 尝试将标头放在没有请求正文的 GET 请求上,该请求正文的内容要描述(通常是当作者混淆 和 时)。Content-Type: application/jsonContent-TypeAccept

在这两种情况下,删除额外的请求标头通常足以避免预检(这将解决与支持简单请求但不支持预检请求的 API 通信时的问题)。


不透明响应(模式)no-cors

有时您需要发出 HTTP 请求,但不需要读取响应。例如,如果您要将日志消息发布到服务器进行记录。

如果您使用的是 fetch API(而不是 ),则可以将其配置为不尝试使用 CORS。XMLHttpRequest

请注意,这不会让您执行任何需要 CORS 执行的操作您将无法阅读响应。您将无法提出需要预检的请求。

它允许您发出简单的请求,看不到响应,也不会在开发人员控制台中填充错误消息。

当您使用 CORS 发出请求并且没有获得查看响应的权限时,会给出 Chrome 错误消息来解释如何做到这一点:fetch

CORS 策略阻止了对从源 '' 提取的 '' 的访问:请求的资源上不存在 '' 标头。如果不透明响应满足您的需求,请将请求的模式设置为“no-cors”,以在禁用 CORS 的情况下获取资源。https://example.com/https://example.netAccess-Control-Allow-Origin

因此:

fetch("http://example.com", { mode: "no-cors" });

CORS 的替代品

JSONP格式

Bob 还可以使用像 JSONP 这样的 hack 来提供数据,这是人们在 CORS 出现之前跨域 Ajax 的方式。

它的工作原理是以 JavaScript 程序的形式呈现数据,该程序将数据注入 Mallory 的页面。

它要求 Mallory 信任 Bob 不会提供恶意代码。

请注意共同的主题:提供数据的网站必须告诉浏览器,第三方网站可以访问它发送到浏览器的数据。

由于 JSONP 的工作原理是附加一个元素以调用页面中已有函数的 JavaScript 程序的形式加载数据,因此尝试在返回 JSON 的 URL 上使用 JSONP 技术将失败(通常出现 CORB 错误),因为 JSON 不是 JavaScript。<script>

将两个资源移动到单个源

如果运行 JS 的 HTML 文档和所请求的 URL 位于同一源上(共享相同的方案、主机名和端口),则默认情况下,同源策略会授予权限。不需要 CORS。

代理

Mallory 可以使用服务器端代码来获取数据(然后她可以像往常一样通过 HTTP 将数据从服务器传递到 Alice 的浏览器)。

它将:

  • 添加 CORS 标头
  • 将响应转换为 JSONP
  • 与 HTML 文档位于同一源上

该服务器端代码可以由第三方(例如CORS Anywhere)编写和托管。请注意这一点对隐私的影响:第三方可以监控谁在其服务器上代理什么。

Bob 无需授予任何权限即可实现此目的。

这里没有安全隐患,因为这只是在 Mallory 和 Bob 之间。Bob 无法认为 Mallory 就是 Alice,也无法向 Mallory 提供 Alice 和 Bob 之间应该保密的数据。

因此,Mallory 只能使用这种技术来读取公共数据。

但是请注意,从他人的网站上获取内容并自行显示可能会侵犯版权,并使您面临法律诉讼。

编写 Web 应用以外的内容

如“为什么同源策略仅适用于网页中的 JavaScript”一节所述,您可以通过不在网页中编写 JavaScript 来避免 SOP。

这并不意味着你不能继续使用 JavaScript 和 HTML,但你可以使用其他机制来分发它,比如 Node-WebKit 或 PhoneGap。

浏览器扩展

在应用同源策略之前,浏览器扩展可以在响应中注入 CORS 标头。

这些对于开发很有用,但对于生产站点来说并不实用(要求站点的每个用户安装禁用其浏览器安全功能的浏览器扩展程序是不合理的)。

它们也往往只处理简单的请求(在处理预检 OPTIONS 请求时失败)。

使用本地开发服务器拥有适当的开发环境通常是更好的方法。


其他安全风险

请注意,SOP/CORS 并不能缓解需要独立处理的 XSSCSRFSQL 注入攻击。


总结

  • 客户端代码中,您无法执行任何操作来启用对其他人服务器的 CORS 访问。
  • 如果您控制向以下服务器发出请求: 向其添加 CORS 权限。
  • 如果您对控制它的人很友好:让他们为其添加 CORS 权限。
  • 如果它是公共服务,那么(请记住,大多数第三方 API 被设计为仅由服务器端代码交互,并且不支持 CORS,但支持那些支持 CORS):
    • 阅读他们的 API 文档,看看他们对使用客户端 JavaScript 访问它的看法:
      • 他们可能会告诉您使用特定的网址
      • 它们可能支持 JSONP 而不是 CORS
      • 它们可能根本不支持来自客户端代码的跨域访问(这可能是出于安全原因的深思熟虑的决定,尤其是当您必须在每个请求中传递个性化的 API 密钥时)。
    • 确保您没有触发不需要的预检请求。API 可能会授予对简单请求的权限,但不会授予预检请求的权限。
  • 如果以上情况均不适用:让浏览器改为与您的服务器通信,然后让您的服务器从其他服务器获取数据并将其传递出去。(还有一些第三方托管服务将 CORS 标头附加到您可以使用的可公开访问的资源)。

评论

0赞 Ciasto piekarz 12/23/2016
如果我运行本地 LAN 和 Web 服务器并尝试从 IP/URL 进行 ajax 加载,那会起作用吗?我还没有尝试过。因为我的 Web 服务器 returing json 数据将是一个 MCU
0赞 Quentin 12/23/2016
@Ciastopiekarz — 适用正常的相同来源/不同来源规则。适用正常的网络路由规则。
0赞 snippetkid 5/11/2017
@Quentin - 哇!+1!所以我要理解的是,如果 Alice 使用 CORS 扩展,服务器认为她的 http 调用不是来自 javascript 而是来自浏览器扩展,并将其视为普通的同源请求?
0赞 Quentin 5/11/2017
@snippetkid — 不。在通常情况下,服务器将在响应中发送 CORS 标头,而不关心请求来自何处。浏览器负责根据响应上的 CORS 标头允许或拒绝对 JS 的数据进行访问。(当涉及到预检请求时,服务器上的事情会变得/有点/复杂)
0赞 Suraj Jain 4/18/2018
“您使用什么语言进行服务器端编程(如果有的话)。”你能帮我澄清一下吗?服务器端编程将需要一种语言。
2赞 Vishnu 2/22/2016 #2

发生这种情况的原因是 CORS 错误。CORS 代表 跨域资源共享。简而言之,当我们尝试从另一个域访问域/资源时,会发生此错误。

在这里阅读更多关于它的信息: jquery的CORS错误

若要解决此问题,如果您有权访问其他域,则必须在服务器中允许 Access-Control-Allow-Origin。这可以添加到标题中。您可以为所有请求/域或特定域启用此功能。

如何使跨域资源共享 (CORS) 发布请求正常工作

这些链接可能会有所帮助

4赞 Daphoque 9/9/2016 #3

目标服务器必须允许跨域请求。为了允许它通过 express,只需处理 http options 请求:

app.options('/url...', function(req, res, next){
   res.header('Access-Control-Allow-Origin', "*");
   res.header('Access-Control-Allow-Methods', 'POST');
   res.header("Access-Control-Allow-Headers", "accept, content-type");
   res.header("Access-Control-Max-Age", "1728000");
   return res.sendStatus(200);
});
-14赞 Perostek Balveda 4/17/2017 #4

您应该启用 CORS 以使其正常工作。

3赞 zwif 11/9/2017 #5

因为这在接受的答案中没有提到。

  • 对于这个确切的问题,情况并非如此,但可能会帮助其他搜索该问题的人
  • 在某些情况下,您可以在客户端代码中执行此操作,以防止 CORS 错误。

您可以使用简单请求
为了执行“简单请求”,请求需要满足几个条件。例如,只允许 和 方法,以及只允许一些给定的 Header(你可以在这里找到所有条件)。
POSTGETHEAD

如果您的客户端代码没有在请求中显式设置受影响的标头(例如“Accept”),则可能会发生某些客户端使用一些“非标准”值自动设置这些标头的情况,从而导致服务器不接受它作为简单请求 - 这将给您带来 CORS 错误。

0赞 morph85 12/17/2018 #6

这个CORS问题没有进一步详细说明(由于其他原因)。

我目前在不同的原因下遇到了这个问题。 我的前端也返回“Access-Control-Allow-Origin”标头错误。

只是我指向了错误的 URL,所以这个标题没有正确反映(我一直认为它确实反映了)。localhost(前端)->调用不安全的 http(应该是 https),请确保前端的 API 端点指向正确的协议。

-1赞 Geshe 11/9/2019 #7

带有附加标头的“Get”请求转换为“Options”请求。因此,发生了 Cors 策略问题。您必须对服务器实现“选项”请求。关于服务器端的 Cors 策略,您需要在服务器端允许 Cors 策略。对于 Nodejs 服务器:details

app.use(cors)

让 Java 与 Angular:details 集成

@CrossOrigin(origins = "http://localhost:4200")
0赞 Subhashi 2/11/2020 #8

我在 Chrome 控制台中遇到了同样的错误。

我的问题是,我试图使用而不是 .所以没有什么可以修复的,只需要使用.http://https://https

0赞 HungNM2 8/20/2020 #9

这个错误花了我 2 天。我检查了我的服务器日志,浏览器 Chrome/Edge 和服务器之间的预检选项请求/响应正常。主要原因是 XHTMLRequest 的 GET/POST/PUT/DELETE 服务器响应还必须具有以下标头:

access-control-allow-origin: origin  

“origin”在请求标头中(浏览器会将其添加到请求中)。例如:

Origin: http://localhost:4221

您可以添加如下所示的响应标头,以便为所有人接受:

access-control-allow-origin: *  

或特定请求的响应标头,例如:

access-control-allow-origin: http://localhost:4221 

浏览器中的消息不清楚理解:“......请求的资源”

请注意:CORS 适用于 localhost。不同的端口意味着不同的域。 如果收到错误消息,请检查服务器端的 CORS 配置。

-1赞 Massimo Pavanel 11/3/2020 #10

在大多数住房服务中,只需在目标服务器文件夹的 .htaccess 中添加以下内容:

标头集 Access-Control-Allow-Origin 'https://your.site.folder'

-1赞 Inshal Irshad 5/8/2021 #11

我有同样的问题。就我而言,我通过向我的 URL 添加添加参数来修复它。即使我正在访问的服务器也不需要这样做。timestamp

示例 yoururl.com/yourdocument?timestamp=1234567

注意:我使用了 epos 时间戳