为什么发送OPTIONS请求并且可以禁用它?

Qia*_*hen 367 http options cors

我正在构建一个Web API.我发现每当我使用Chrome进行POST,GET到我的API时,总会在真实请求之前发送OPTIONS请求,这非常烦人.目前我让服务器忽略任何OPTIONS请求.现在我的问题是发送OPTIONS请求以加倍服务器负载是什么好事?有没有办法完全阻止浏览器发送OPTIONS请求?

Leo*_*rea 339

编辑2018-09-13:增加了关于此飞行前请求的一些准确性以及如何在此响应结束时避免它.

OPTIONS请求是我们称之为的pre-flight请求Cross-origin resource sharing (CORS).

当您在特定情况下跨不同来源提出请求时,它们是必需的.

某些浏览器将此飞行前请求作为安全措施,以确保服务器信任所完成的请求.这意味着服务器了解在请求上发送的方法,来源和标头是安全的.

当您尝试执行跨源请求时,您的服务器不应忽略但处理这些请求.

这里有一个很好的资源http://enable-cors.org/

处理这些以使其变得舒适的一种方法是确保对于具有OPTIONS方法的任何路径,服务器使用此标头发送响应

Access-Control-Allow-Origin: *

这将告诉浏览器服务器愿意回答来自任何来源的请求.

有关如何向服务器添加CORS支持的更多信息,请参阅以下流程图

http://www.html5rocks.com/static/images/cors_server_flowchart.png

CORS流程图


编辑2018-09-13

CORS OPTIONS请求仅在某些情况下触发,如MDN文档中所述:

有些请求不会触发CORS预检.这些在本文中称为"简单请求",尽管Fetch规范(定义CORS)不使用该术语.不触发CORS预检的请求 - 所谓的"简单请求" - 满足以下所有条件的请求:

唯一允许的方法是:

  • 得到
  • POST

除了由用户代理自动设置的标头(例如,连接,用户代理或任何其他标头,其中名称在Fetch规范中定义为"禁用标头名称")之外,唯一允许的标头手动设置是Fetch规范定义为"CORS-safelisted request-header"的那些,它们是:

  • 接受
  • 接受语言
  • 内容语言
  • 内容类型(但请注意下面的附加要求)
  • DPR
  • 下行
  • 保存数据
  • 视口宽度
  • 宽度

Content-Type标头唯一允许的值是:

  • 应用程序/ x-WWW窗体-urlencoded
  • 多部分/格式数据
  • 纯文本/

在请求中使用的任何XMLHttpRequestUpload对象上都没有注册事件侦听器; 这些是使用XMLHttpRequest.upload属性访问的.

请求中不使用ReadableStream对象.

  • 在发出跨越原始请求时,要求预检请求是不正确的.只有在特定情况下才需要预检请求,例如,如果您要设置自定义标头,或者提出get,head和post之外的请求. (29认同)
  • 但是将Chrome标记设置为所有普通用户并不现实. (8认同)
  • @SuperUberDuper因为CORS和预检请求是浏览器相关的事情.您可以通过在请求中添加"Origin"标头来模拟CORS,以模拟请求来自特定主机(例如yourwebsite.com).您还可以通过将请求的HTTP方法设置为"OPTIONS"和"Access-Control-*"标题来模拟预检请求 (5认同)
  • 有趣的是,当使用jQuery发出CORS请求时,JavaScript库特别避免设置自定义标题,并向开发人员发出警告:_对于跨域请求,看到预检的条件类似于拼图游戏,我们根本就没有把它设置为确定._ (4认同)
  • 如果我对它起作用的api进行`curl`怎么办,但是从chrome运行时出现错误? (3认同)
  • 对于 Web 应用程序用例来说,说“仅在某些情况下才会触发 CORS OPTIONS 请求”是一种严重的轻描淡写。您很少会发送不带“Authorization”标头的 API 请求和/或使用“application/json”作为内容类型,这意味着有时不需要 OPTIONS 请求,实际上总是需要它们。在数据量大的 Web 应用程序中,这意味着当前您“必须”从与应用程序相同的主机提供 API,以避免 OPTIONS 请求,这很遗憾,因为在许多用例中,使用不同的主机是有意义的。 (3认同)
  • @LeoCorrea因为您的问题在SO和Google上很受欢迎,所以我已经更新了答案,其中包含了一些关于浏览器未发送飞行前请求的准确性,2018年;) (2认同)

Nee*_*key 213

经历过这个问题,下面是我对这个问题和我的解决方案的结论.

根据CORS策略(强烈建议你阅读它)你不能强迫浏览器停止发送OPTION请求,如果它认为需要.

有两种方法可以解决它

    1. 确保您的请求是"简单请求"
    1. Access-Control-Max-Age为OPTION请求设置

简单的要求

一个简单的跨站点请求是满足以下所有条件的请求:

唯一允许的方法是:

  • 得到
  • POST

除了用户代理自动设置的标头(例如Connection,User-Agent等)之外,允许手动设置的唯一标头是:

  • 接受
  • 接受语言
  • 内容语言
  • 内容类型

Content-Type标头唯一允许的值是:

  • 应用程序/ x-WWW窗体-urlencoded
  • 多部分/格式数据
  • 纯文本/

简单的请求不会导致飞行前的OPTION请求.

为OPTION检查设置缓存

您可以Access-Control-Max-Age为OPTION请求设置一个,以便它在过期之前不会再次检查权限.

Access-Control-Max-Age以秒为单位给出了在不发送其他预检请求的情况下可以缓存对预检请求的响应的时间长度的值.

  • IMO,这是这个帖子中最清晰的答案. (41认同)
  • 感谢您提及`Access-Control-Max-Age`.这是关键所在.它可以帮助您避免过多的预检请求. (5认同)
  • 是的......这应该是接受的答案,与问题最相关..! (3认同)
  • @VitalyZdanevich 不!不要仅仅因为它使您的请求不“简单”(从而触发 CORS)而避免使用 `application/json`。浏览器正在做它的工作。将您的服务器设置为返回诸如“Access-Control-Max-Age: 86400”之类的标头,浏览器将不会在 24 小时内重新发送 OPTIONS 请求。 (3认同)

小智 139

请参考这个答案,了解预先确定的OPTIONS请求的实际需求:CORS - 引入预检请求背后的动机是什么?

要禁用OPTIONS请求,必须满足以下条件:ajax请求:

  1. 请求不会设置自定义HTTP标头,如'application/xml'或'application/json'等
  2. 请求方法必须是GET,HEAD或POST之一.如果POST,内容类型应该是一个application/x-www-form-urlencoded,multipart/form-datatext/plain

参考:https: //developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

  • `application/xml`或`application/json`不是"自定义HTTP标头".标题本身是`Content-Type`并且调用标题"custom"会产生误导. (21认同)
  • +1"自定义HTTP标头"!就我而言,他们导致了飞行前的请求被触发.当请求主体和OPTIONS请求停止发送时,我重构了发送在头中发送的任何内容的请求. (14认同)

Nir*_*Nir 46

打开调试控制台并打开Disable Cache选项后,将始终发送预检请求(即在每个请求之前).如果您不禁用缓存,则只会发送一次飞行前请求(每台服务器)

  • 哦,我在想什么.调试几个小时这是我的解决方案.由于调试控制台而禁用缓存. (3认同)
  • 即使调试控制台关闭,也会发送预检请求 (2认同)
  • Luca:的确如此,但要点是,关闭开发工具后,“禁用缓存”无效。如果未禁用缓存,则预检请求仅发送一次(当然,每个服务器)(如果禁用了缓存,则在每个请求之前发送)。 (2认同)

use*_*349 41

是的,可以避免选项请求.当您将任何数据发送(发布)到另一个域时,选项请求是预检请求.这是一个浏览器安全问题.但我们可以使用另一种技术:iframe传输层.我强烈建议您忘记任何CORS配置并使用现成的解决方案,它可以在任何地方使用.

看看这里:https: //github.com/jpillora/xdomain

工作示例:http: //jpillora.com/xdomain/

祝你今天愉快!

  • "当您将任何数据发送(发布)到另一个域时,选项请求是一个预检请求." - 这不是真的.您可以使用XHR发送可以使用普通HTML表单发送的任何POST请求,而不会触发预检请求.只有当您开始执行表单无法执行的操作(如自定义内容类型或额外请求标头)时,才会发送预检. (12认同)
  • iFrame并非为此而设计。 (3认同)

Jos*_*nke 15

对于了解其存在的原因但需要访问不使用auth处理OPTIONS调用的API的开发人员,我需要一个临时答案,以便我可以在本地开发,直到API所有者添加适当的SPA CORS支持或我获得代理API启动并运行.

我发现你可以在Safari上禁用CORS,在Mac上禁用Chrome.

在Chrome中停用相同的来源政策

Chrome:退出Chrome,打开终端并粘贴此命令: open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir

Safari:在Safari中禁用同源策略

如果要在Safari上禁用同源策略(我有9.1.1),则只需启用开发人员菜单,然后从开发菜单中选择"禁用跨源限制".

  • 您应该在标有**的部分上加亮点,“这绝对不是永久解决方案!!!” **。相同来源策略是一种非常重要的浏览器安全措施,在正常浏览Internet时切勿禁用该策略。 (2认同)

enp*_*nax 14

正如之前的帖子中所提到的,OPTIONS请求是有原因的.如果您的服务器响应时间很长(例如海外连接),您也可以让浏览器缓存预检请求.

让您的服务器回复Access-Control-Max-Age标题,对于转到同一端点的请求,预检请求将被缓存而不再发生.


Ant*_*iCZ 8

我已经解决了这个问题.

if($_SERVER['REQUEST_METHOD'] == 'OPTIONS' && ENV == 'devel') {
    header('Access-Control-Allow-Origin: *');
    header('Access-Control-Allow-Headers: X-Requested-With');
    header("HTTP/1.1 200 OK");
    die();
}
Run Code Online (Sandbox Code Playgroud)

它只是为了发展.有了这个我等待9ms和500ms而不是8s和500ms.我可以这样做,因为生产JS应用程序将与生产在同一台机器上,所以没有,OPTIONS但开发是我的本地.


Jos*_*ato 5

你不能但是你可以使用JSONP来避免使用CORS.

  • 如果您正在做某事 [不简单](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests),您只会收到 OPTIONS 请求。您只能使用 JSONP 进行简单的请求(GET、无自定义标头、无身份验证数据),因此 JSONP 无法在这里替代。 (5认同)