为什么会发送 OPTIONS 请求,我可以禁用它吗?
Why is an OPTIONS request sent and can I disable it?
我正在构建一个网络 API。我发现每当我使用 Chrome 到 POST,GET 到我的 API 时,总是在真正的请求之前发送一个 OPTIONS 请求,这很烦人。目前,我让服务器忽略任何 OPTIONS 请求。现在我的问题是发送一个 OPTIONS 请求来加倍服务器的负载有什么好处?有没有办法完全阻止浏览器发送OPTIONS请求?
编辑 2018-09-13:添加了一些关于此 pre-flight 请求的精确度以及如何在此响应末尾避免它。
OPTIONS
请求就是我们在Cross-origin resource sharing (CORS)
.
中所说的pre-flight
请求
当您在特定情况下跨不同来源发出请求时,它们是必需的。
此 pre-flight 请求是由某些浏览器作为安全措施发出的,以确保所完成的请求得到服务器的信任。
这意味着服务器知道根据请求发送的方法、来源和 headers 是可以安全采取行动的。
每当您尝试执行跨源请求时,您的服务器不应忽略而是处理这些请求。
这里可以找到很好的资源http://enable-cors.org/
一种处理这些问题的方法是确保对于任何使用 OPTIONS
方法的路径,服务器都会发送带有此 header
的响应
Access-Control-Allow-Origin: *
这将告诉浏览器服务器愿意回答来自任何来源的请求。
有关如何向您的服务器添加 CORS 支持的更多信息,请参阅以下流程图
http://www.html5rocks.com/static/images/cors_server_flowchart.png
编辑 2018-09-13
CORS OPTIONS
请求仅在某些情况下触发,如 MDN docs:
中所述
Some requests don’t trigger a CORS preflight. Those are called “simple requests” in this article, though the Fetch spec (which defines CORS) doesn’t use that term. A request that doesn’t trigger a CORS preflight—a so-called “simple request”—is one that meets all the following conditions:
The only allowed methods are:
- GET
- HEAD
- POST
Apart from the headers set automatically by the user agent (for example, Connection, User-Agent, or any of the other headers with names defined in the Fetch spec as a “forbidden header name”), the only headers which are allowed to be manually set are those which the Fetch spec defines as being a “CORS-safelisted request-header”, which are:
- Accept
- Accept-Language
- Content-Language
- Content-Type (but note the additional requirements below)
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width
The only allowed values for the Content-Type header are:
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
No event listeners are registered on any XMLHttpRequestUpload object used in the request; these are accessed using the XMLHttpRequest.upload property.
No ReadableStream object is used in the request.
你不能,但你可以使用 JSONP 避免 CORS。
是的,可以避免选项请求。当您向另一个域发送 (post) 任何数据时,选项请求是预检请求。这是一个浏览器安全问题。但是我们可以使用另一种技术:iframe传输层。我强烈建议您忘记任何 CORS 配置并使用现成的解决方案,它可以在任何地方工作。
看看这里:
https://github.com/jpillora/xdomain
和工作示例:
http://jpillora.com/xdomain/
请根据pre-flighted OPTIONS 请求的实际需要参考此答案:CORS - What is the motivation behind introducing preflight requests?
要禁用 OPTIONS 请求,ajax 请求必须满足以下条件:
- 请求未设置自定义 HTTP headers,如 'application/xml' 或 'application/json' 等
- 请求方法必须是 GET、HEAD 或 POST 之一。如果 POST,内容类型应为
application/x-www-form-urlencoded
、multipart/form-data
或 text/plain
之一
参考:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
正如之前的帖子所述,OPTIONS
请求是有原因的。如果您的服务器响应时间过长(例如海外连接),您还可以让浏览器缓存预检请求。
让您的服务器回复 Access-Control-Max-Age
header 并且对于到达同一端点的请求,预检请求将被缓存并且不再发生。
当您打开调试控制台并打开 Disable Cache
选项时,将始终发送预检请求(即在每个请求之前)。如果您不禁用缓存,飞行前请求将仅发送一次(每个服务器)
我已经解决了这个问题。
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();
}
仅供开发使用。有了这个,我等待 9 毫秒和 500 毫秒,而不是 8 秒和 500 毫秒。我可以这样做,因为生产 JS 应用程序将与生产在同一台机器上,所以不会 OPTIONS
但开发是我的本地。
经历过这个问题,下面是我对这个问题的总结和解决方案。
根据 CORS strategy(强烈建议您阅读),如果浏览器认为有必要,您不能强制其停止发送 OPTIONS 请求。
有两种解决方法:
- 确保您的请求是 "simple request"
- 为 OPTIONS 请求设置
Access-Control-Max-Age
简单请求
一个简单的cross-site请求是满足以下所有条件的请求:
唯一允许的方法是:
- 得到
- 头部
- POST
除了由用户代理自动设置的 headers(例如连接、User-Agent 等),唯一允许手动设置的 headers 是:
- 接受
- Accept-Language
- Content-Language
- Content-Type
Content-Type header 的唯一允许值是:
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
一个简单的请求不会引起 pre-flight OPTIONS 请求。
为 OPTIONS 检查设置缓存
你可以为OPTIONS请求设置一个Access-Control-Max-Age
,这样它就不会在过期之前再次检查权限。
Access-Control-Max-Age gives the value in seconds for how long the response to the preflight request can be cached for without sending another preflight request.
注意限制
- 对于 Chrome,
Access-Control-Max-Age
的最大秒数是 600
,根据 chrome source code ,即 10 分钟
Access-Control-Max-Age
每次只对一个资源起作用,例如GET
请求具有相同的URL路径但不同的查询将被视为不同的资源。所以对第二个资源的请求还是会触发预检请求。
对于理解它存在的原因但需要访问一个 API 的开发人员来说,它不会在未经授权的情况下处理 OPTIONS 调用,我需要一个临时答案,以便我可以在本地进行开发,直到 API 所有者添加适当的 SPA CORS 支持,或者我得到代理 API 和 运行。
我发现您可以在 Safari 中禁用 CORS 并在 Mac 上 Chrome。
Disable same origin policy in Chrome
Chrome:退出 Chrome,打开终端并粘贴此命令:open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir
野生动物园:Disabling same-origin policy in Safari
If you want to disable the same-origin policy on Safari (I have 9.1.1), then you only need to enable the developer menu, and select "Disable Cross-Origin Restrictions" from the develop menu.
对我有用的是导入 "github.com/gorilla/handlers" 然后这样使用它:
router := mux.NewRouter()
router.HandleFunc("/config", getConfig).Methods("GET")
router.HandleFunc("/config/emcServer", createEmcServers).Methods("POST")
headersOk := handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type"})
originsOk := handlers.AllowedOrigins([]string{"*"})
methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"})
log.Fatal(http.ListenAndServe(":" + webServicePort, handlers.CORS(originsOk, headersOk, methodsOk)(router)))
一旦我执行 Ajax POST 请求并向其附加 JSON 数据,Chrome 将始终添加 Content-Type header 这不在我之前的 AllowedHeaders 配置中。
我过去使用过的一个解决方案 - 假设您的网站在 mydomain.com 上,您需要向 foreigndomain.com
发出 ajax 请求
配置从您的域到外部域的 IIS 重写 - 例如
<rewrite>
<rules>
<rule name="ForeignRewrite" stopProcessing="true">
<match url="^api/v1/(.*)$" />
<action type="Rewrite" url="https://foreigndomain.com/{R:1}" />
</rule>
</rules>
</rewrite>
在您的 mydomain.com 站点上 - 然后您可以发出同源请求,并且不需要任何选项请求:)
在花了一整天半的时间尝试解决类似问题后,我发现它与 IIS 有关。
我的 Web API 项目设置如下:
// WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
//...
}
我在 web.config > system.webServer 节点中没有 CORS 特定的配置选项,就像我在很多帖子中看到的那样
global.asax 或控制器中没有 CORS 特定代码作为装饰器
问题出在应用程序池设置。
托管管道模式设置为经典(将其更改为集成)和Identity 设置为网络服务(将其更改为 ApplicationPoolIdentity)
更改这些设置(并刷新应用程序池)为我修复了它。
如果使用代理拦截请求并编写适当的 headers,则可以解决。
在清漆的特殊情况下,这些规则是:
if (req.http.host == "CUSTOM_URL" ) {
set resp.http.Access-Control-Allow-Origin = "*";
if (req.method == "OPTIONS") {
set resp.http.Access-Control-Max-Age = "1728000";
set resp.http.Access-Control-Allow-Methods = "GET, POST, PUT, DELETE, PATCH, OPTIONS";
set resp.http.Access-Control-Allow-Headers = "Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since";
set resp.http.Content-Length = "0";
set resp.http.Content-Type = "text/plain charset=UTF-8";
set resp.status = 204;
}
}
也许有一个解决方案(但我没有测试):您可以使用 CSP(内容安全策略)来启用您的远程域,浏览器可能会跳过 CORS OPTIONS 请求验证。
如果有时间,我会测试并更新这个 post !
CSP:https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Content-Security-Policy
CSP 规范:https://www.w3.org/TR/CSP/
OPTIONS 请求是网络浏览器的一项功能,因此禁用它并不容易。但是我找到了一种方法 redirect 它与 proxy.它在服务端点无法处理 CORS/OPTIONS 的情况下很有用,可能仍在开发中,或者配置错误。
步骤:
- 使用选择的工具(nginx、YARP 等)为此类请求设置反向代理
- 创建一个端点来处理 OPTIONS 请求。创建一个普通的空端点可能更容易,并确保它能很好地处理 CORS。
- 为代理配置两套规则。一种是将所有 OPTIONS 请求路由到上面的虚拟端点。另一个将所有其他请求路由到有问题的实际端点。
- 更新网站以改用代理。
基本上这种方法是欺骗浏览器让 OPTIONS 请求起作用。考虑到 CORS 不是为了加强安全性,而是放宽同源策略,希望这个技巧能管用一段时间。 :)
我正在构建一个网络 API。我发现每当我使用 Chrome 到 POST,GET 到我的 API 时,总是在真正的请求之前发送一个 OPTIONS 请求,这很烦人。目前,我让服务器忽略任何 OPTIONS 请求。现在我的问题是发送一个 OPTIONS 请求来加倍服务器的负载有什么好处?有没有办法完全阻止浏览器发送OPTIONS请求?
编辑 2018-09-13:添加了一些关于此 pre-flight 请求的精确度以及如何在此响应末尾避免它。
OPTIONS
请求就是我们在Cross-origin resource sharing (CORS)
.
pre-flight
请求
当您在特定情况下跨不同来源发出请求时,它们是必需的。
此 pre-flight 请求是由某些浏览器作为安全措施发出的,以确保所完成的请求得到服务器的信任。 这意味着服务器知道根据请求发送的方法、来源和 headers 是可以安全采取行动的。
每当您尝试执行跨源请求时,您的服务器不应忽略而是处理这些请求。
这里可以找到很好的资源http://enable-cors.org/
一种处理这些问题的方法是确保对于任何使用 OPTIONS
方法的路径,服务器都会发送带有此 header
Access-Control-Allow-Origin: *
这将告诉浏览器服务器愿意回答来自任何来源的请求。
有关如何向您的服务器添加 CORS 支持的更多信息,请参阅以下流程图
http://www.html5rocks.com/static/images/cors_server_flowchart.png
编辑 2018-09-13
CORS OPTIONS
请求仅在某些情况下触发,如 MDN docs:
Some requests don’t trigger a CORS preflight. Those are called “simple requests” in this article, though the Fetch spec (which defines CORS) doesn’t use that term. A request that doesn’t trigger a CORS preflight—a so-called “simple request”—is one that meets all the following conditions:
The only allowed methods are:
- GET
- HEAD
- POST
Apart from the headers set automatically by the user agent (for example, Connection, User-Agent, or any of the other headers with names defined in the Fetch spec as a “forbidden header name”), the only headers which are allowed to be manually set are those which the Fetch spec defines as being a “CORS-safelisted request-header”, which are:
- Accept
- Accept-Language
- Content-Language
- Content-Type (but note the additional requirements below)
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width
The only allowed values for the Content-Type header are:
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
No event listeners are registered on any XMLHttpRequestUpload object used in the request; these are accessed using the XMLHttpRequest.upload property.
No ReadableStream object is used in the request.
你不能,但你可以使用 JSONP 避免 CORS。
是的,可以避免选项请求。当您向另一个域发送 (post) 任何数据时,选项请求是预检请求。这是一个浏览器安全问题。但是我们可以使用另一种技术:iframe传输层。我强烈建议您忘记任何 CORS 配置并使用现成的解决方案,它可以在任何地方工作。
看看这里: https://github.com/jpillora/xdomain
和工作示例: http://jpillora.com/xdomain/
请根据pre-flighted OPTIONS 请求的实际需要参考此答案:CORS - What is the motivation behind introducing preflight requests?
要禁用 OPTIONS 请求,ajax 请求必须满足以下条件:
- 请求未设置自定义 HTTP headers,如 'application/xml' 或 'application/json' 等
- 请求方法必须是 GET、HEAD 或 POST 之一。如果 POST,内容类型应为
application/x-www-form-urlencoded
、multipart/form-data
或text/plain
之一
参考: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
正如之前的帖子所述,OPTIONS
请求是有原因的。如果您的服务器响应时间过长(例如海外连接),您还可以让浏览器缓存预检请求。
让您的服务器回复 Access-Control-Max-Age
header 并且对于到达同一端点的请求,预检请求将被缓存并且不再发生。
当您打开调试控制台并打开 Disable Cache
选项时,将始终发送预检请求(即在每个请求之前)。如果您不禁用缓存,飞行前请求将仅发送一次(每个服务器)
我已经解决了这个问题。
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();
}
仅供开发使用。有了这个,我等待 9 毫秒和 500 毫秒,而不是 8 秒和 500 毫秒。我可以这样做,因为生产 JS 应用程序将与生产在同一台机器上,所以不会 OPTIONS
但开发是我的本地。
经历过这个问题,下面是我对这个问题的总结和解决方案。
根据 CORS strategy(强烈建议您阅读),如果浏览器认为有必要,您不能强制其停止发送 OPTIONS 请求。
有两种解决方法:
- 确保您的请求是 "simple request"
- 为 OPTIONS 请求设置
Access-Control-Max-Age
简单请求
一个简单的cross-site请求是满足以下所有条件的请求:
唯一允许的方法是:
- 得到
- 头部
- POST
除了由用户代理自动设置的 headers(例如连接、User-Agent 等),唯一允许手动设置的 headers 是:
- 接受
- Accept-Language
- Content-Language
- Content-Type
Content-Type header 的唯一允许值是:
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
一个简单的请求不会引起 pre-flight OPTIONS 请求。
为 OPTIONS 检查设置缓存
你可以为OPTIONS请求设置一个Access-Control-Max-Age
,这样它就不会在过期之前再次检查权限。
Access-Control-Max-Age gives the value in seconds for how long the response to the preflight request can be cached for without sending another preflight request.
注意限制
- 对于 Chrome,
Access-Control-Max-Age
的最大秒数是600
,根据 chrome source code ,即 10 分钟
Access-Control-Max-Age
每次只对一个资源起作用,例如GET
请求具有相同的URL路径但不同的查询将被视为不同的资源。所以对第二个资源的请求还是会触发预检请求。
对于理解它存在的原因但需要访问一个 API 的开发人员来说,它不会在未经授权的情况下处理 OPTIONS 调用,我需要一个临时答案,以便我可以在本地进行开发,直到 API 所有者添加适当的 SPA CORS 支持,或者我得到代理 API 和 运行。
我发现您可以在 Safari 中禁用 CORS 并在 Mac 上 Chrome。
Disable same origin policy in Chrome
Chrome:退出 Chrome,打开终端并粘贴此命令:open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir
野生动物园:Disabling same-origin policy in Safari
If you want to disable the same-origin policy on Safari (I have 9.1.1), then you only need to enable the developer menu, and select "Disable Cross-Origin Restrictions" from the develop menu.
对我有用的是导入 "github.com/gorilla/handlers" 然后这样使用它:
router := mux.NewRouter()
router.HandleFunc("/config", getConfig).Methods("GET")
router.HandleFunc("/config/emcServer", createEmcServers).Methods("POST")
headersOk := handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type"})
originsOk := handlers.AllowedOrigins([]string{"*"})
methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"})
log.Fatal(http.ListenAndServe(":" + webServicePort, handlers.CORS(originsOk, headersOk, methodsOk)(router)))
一旦我执行 Ajax POST 请求并向其附加 JSON 数据,Chrome 将始终添加 Content-Type header 这不在我之前的 AllowedHeaders 配置中。
我过去使用过的一个解决方案 - 假设您的网站在 mydomain.com 上,您需要向 foreigndomain.com
发出 ajax 请求配置从您的域到外部域的 IIS 重写 - 例如
<rewrite>
<rules>
<rule name="ForeignRewrite" stopProcessing="true">
<match url="^api/v1/(.*)$" />
<action type="Rewrite" url="https://foreigndomain.com/{R:1}" />
</rule>
</rules>
</rewrite>
在您的 mydomain.com 站点上 - 然后您可以发出同源请求,并且不需要任何选项请求:)
在花了一整天半的时间尝试解决类似问题后,我发现它与 IIS 有关。
我的 Web API 项目设置如下:
// WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
//...
}
我在 web.config > system.webServer 节点中没有 CORS 特定的配置选项,就像我在很多帖子中看到的那样
global.asax 或控制器中没有 CORS 特定代码作为装饰器
问题出在应用程序池设置。
托管管道模式设置为经典(将其更改为集成)和Identity 设置为网络服务(将其更改为 ApplicationPoolIdentity)
更改这些设置(并刷新应用程序池)为我修复了它。
如果使用代理拦截请求并编写适当的 headers,则可以解决。 在清漆的特殊情况下,这些规则是:
if (req.http.host == "CUSTOM_URL" ) {
set resp.http.Access-Control-Allow-Origin = "*";
if (req.method == "OPTIONS") {
set resp.http.Access-Control-Max-Age = "1728000";
set resp.http.Access-Control-Allow-Methods = "GET, POST, PUT, DELETE, PATCH, OPTIONS";
set resp.http.Access-Control-Allow-Headers = "Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since";
set resp.http.Content-Length = "0";
set resp.http.Content-Type = "text/plain charset=UTF-8";
set resp.status = 204;
}
}
也许有一个解决方案(但我没有测试):您可以使用 CSP(内容安全策略)来启用您的远程域,浏览器可能会跳过 CORS OPTIONS 请求验证。
如果有时间,我会测试并更新这个 post !
CSP:https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Content-Security-Policy
CSP 规范:https://www.w3.org/TR/CSP/
OPTIONS 请求是网络浏览器的一项功能,因此禁用它并不容易。但是我找到了一种方法 redirect 它与 proxy.它在服务端点无法处理 CORS/OPTIONS 的情况下很有用,可能仍在开发中,或者配置错误。
步骤:
- 使用选择的工具(nginx、YARP 等)为此类请求设置反向代理
- 创建一个端点来处理 OPTIONS 请求。创建一个普通的空端点可能更容易,并确保它能很好地处理 CORS。
- 为代理配置两套规则。一种是将所有 OPTIONS 请求路由到上面的虚拟端点。另一个将所有其他请求路由到有问题的实际端点。
- 更新网站以改用代理。
基本上这种方法是欺骗浏览器让 OPTIONS 请求起作用。考虑到 CORS 不是为了加强安全性,而是放宽同源策略,希望这个技巧能管用一段时间。 :)