¿Por qué se envía una solicitud de OPCIONES y puedo desactivarla?

415

Estoy construyendo una API web. Descubrí que cada vez que uso Chrome para PUBLICAR, LLEGAR a mi API, siempre hay una solicitud de OPCIONES enviada antes de la solicitud real, lo cual es bastante molesto. Actualmente obtengo que el servidor ignore cualquier solicitud de OPCIONES. Ahora mi pregunta es ¿qué tiene de bueno enviar una solicitud de OPCIONES para duplicar la carga del servidor? ¿Hay alguna forma de evitar que el navegador envíe solicitudes de OPCIONES por completo?

Qian Chen
fuente

Respuestas:

376

editar 2018-09-13 : se agregaron algunas precisiones sobre esta solicitud previa al vuelo y cómo evitarla al final de esta respuesta.

OPTIONSlas solicitudes son lo que llamamos pre-flightsolicitudes en Cross-origin resource sharing (CORS).

Son necesarios cuando realiza solicitudes a través de diferentes orígenes en situaciones específicas.

Algunos navegadores realizan esta solicitud previa al vuelo como medida de seguridad para garantizar que el servidor confíe en la solicitud que se realiza. Lo que significa que el servidor entiende que el método, el origen y los encabezados que se envían en la solicitud son seguros para actuar.

Su servidor no debe ignorar sino manejar estas solicitudes siempre que intente hacer solicitudes de origen cruzado.

Un buen recurso se puede encontrar aquí http://enable-cors.org/

Una forma de manejarlos para sentirse cómodo es asegurarse de que, para cualquier ruta con OPTIONSmétodo, el servidor envíe una respuesta con este encabezado

Access-Control-Allow-Origin: *

Esto le dirá al navegador que el servidor está dispuesto a responder solicitudes de cualquier origen.

Para obtener más información sobre cómo agregar soporte CORS a su servidor, consulte el siguiente diagrama de flujo

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

Diagrama de flujo de CORS


editar 2018-09-13

La OPTIONSsolicitud CORS se activa solo en algunos casos, como se explica en los documentos de MDN :

Algunas solicitudes no activan una verificación previa CORS. Esas se llaman "solicitudes simples" en este artículo, aunque la especificación Fetch (que define CORS) no usa ese término. Una solicitud que no desencadena una verificación previa de CORS, una llamada "solicitud simple", es una que cumple con las siguientes condiciones:

Los únicos métodos permitidos son:

  • OBTENER
  • CABEZA
  • ENVIAR

Además de los encabezados configurados automáticamente por el agente de usuario (por ejemplo, Connection, User-Agent o cualquiera de los otros encabezados con nombres definidos en la especificación Fetch como "nombre de encabezado prohibido"), los únicos encabezados que pueden configurados manualmente son aquellos que la especificación Fetch define como un "encabezado de solicitud protegido por CORS", que son:

  • Aceptar
  • Aceptar lenguaje
  • Lenguaje de contenido
  • Tipo de contenido (pero tenga en cuenta los requisitos adicionales a continuación)
  • DPR
  • Enlace descendente
  • Guardar datos
  • Ancho de ventana
  • Anchura

Los únicos valores permitidos para el encabezado Content-Type son:

  • application / x-www-form-urlencoded
  • multipart / form-data
  • Texto sin formato

Ningún detector de eventos está registrado en ningún objeto XMLHttpRequestUpload utilizado en la solicitud; se accede a ellos utilizando la propiedad XMLHttpRequest.upload.

No se utiliza ningún objeto ReadableStream en la solicitud.

Leo Correa
fuente
8
Pero no es realista establecer esta bandera de Chrome para todos los usuarios generales.
Qian Chen
37
Es incorrecto decir que se requieren solicitudes de verificación previa al realizar solicitudes de origen cruzado. Las solicitudes de verificación previa solo se requieren en situaciones específicas, como si está configurando encabezados personalizados o haciendo solicitudes que no sean get, head y post.
Robin Clowers
44
Curiosamente, al realizar una solicitud CORS utilizando jQuery, la biblioteca de JavaScript evita específicamente establecer el encabezado personalizado, junto con una advertencia para los desarrolladores: para solicitudes entre dominios, ya que las condiciones para una verificación previa son similares a un rompecabezas, simplemente nunca lo configure para estar seguro.
Debajo del radar
3
¿Cómo es que si hago un curla la API funciona, pero cuando se ejecuta desde Chrome me sale el error?
SuperUberDuper
55
@SuperUberDuper porque CORS y las solicitudes de verificación previa son un asunto relacionado con el navegador. Puede simular CORS agregando un Originencabezado a su solicitud para simular como si la solicitud proveniera de un host específico (por ejemplo, yourwebsite.com). También puede simular solicitudes de verificación previa configurando el método HTTP de una solicitud OPTIONSy los Access-Control-*Encabezados
Leo Correa
234

He pasado por este problema, a continuación está mi conclusión a este problema y mi solución.

De acuerdo con la estrategia CORS (le recomiendo que lea al respecto) No puede simplemente forzar al navegador a dejar de enviar la solicitud de OPCIONES si cree que es necesario.

Hay dos formas de solucionarlo:

  1. Asegúrese de que su solicitud sea una "solicitud simple"
  2. Establecer Access-Control-Max-Agepara la solicitud de OPCIONES

Solicitud simple

Una solicitud simple entre sitios es aquella que cumple con todas las siguientes condiciones:

Los únicos métodos permitidos son:

  • OBTENER
  • CABEZA
  • ENVIAR

Además de los encabezados configurados automáticamente por el agente de usuario (por ejemplo, Connection, User-Agent, etc.), los únicos encabezados que se pueden configurar manualmente son:

  • Aceptar
  • Aceptar lenguaje
  • Lenguaje de contenido
  • Tipo de contenido

Los únicos valores permitidos para el encabezado Content-Type son:

  • application / x-www-form-urlencoded
  • multipart / form-data
  • Texto sin formato

Una solicitud simple no causará una solicitud de OPCIONES previas al vuelo.

Establecer un caché para la verificación de OPCIONES

Puede configurar un Access-Control-Max-Agepara la solicitud de OPCIONES, para que no vuelva a comprobar el permiso hasta que caduque.

Access-Control-Max-Age proporciona el valor en segundos durante cuánto tiempo se puede almacenar en caché la respuesta a la solicitud de verificación previa sin enviar otra solicitud de verificación previa.

Limitación observada

  • Para Chrome, los segundos máximos para Access-Control-Max-Agedecir 600que es de 10 minutos, de acuerdo con cromo código fuente
  • Access-Control-Max-Agesolo funciona para un recurso cada vez, por ejemplo, las GETsolicitudes con la misma ruta URL pero las consultas diferentes se tratarán como recursos diferentes. Por lo tanto, la solicitud al segundo recurso seguirá activando una solicitud de verificación previa.
Neekey
fuente
3
Sí ... esta debería ser la respuesta aceptada y más relevante para la pregunta ...!
Rajesh Mbm
77
Gracias por mencionar que Access-Control-Max-Age. Esa es la clave aquí. Le ayuda a evitar excesivas solicitudes de verificación previa.
Idris Mokhtarzada
Estoy usando axios para llamar a obtener solicitud. ¿Dónde puedo configurar Access-Control-Max-Age en la solicitud de axios?
Mohit Shah
+1 El encabezado Access-Control-Max-Age es la clave aquí. ¡Esta debería ser la respuesta aceptada! Configuré 86400 segundos (24 horas) en el encabezado y la solicitud previa se ha ido.
revobtz
1
@VitalyZdanevich no! No lo evite application/jsonsolo porque hace que su solicitud no sea "simple" (y por lo tanto desencadena CORS). El navegador está haciendo su trabajo. Configure su servidor para que devuelva algo como un encabezado Access-Control-Max-Age: 86400y el navegador no reenviará una solicitud de OPCIONES durante 24 horas.
colm.anseo
139

Consulte esta respuesta sobre la necesidad real de solicitud de OPCIONES pre-voladas: CORS - ¿Cuál es la motivación detrás de la introducción de solicitudes de verificación previa?

Para deshabilitar la solicitud OPTIONS, se deben cumplir las siguientes condiciones para la solicitud ajax:

  1. La solicitud no establece encabezados HTTP personalizados como 'application / xml' o 'application / json', etc.
  2. El método de solicitud debe ser GET, HEAD o POST. Si la POST, tipo de contenido debe ser uno de application/x-www-form-urlencoded, multipart/form-dataotext/plain

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

device_exec
fuente
14
¡+1 para "encabezados HTTP personalizados"! En mi caso, estaban causando la activación de la solicitud previa al vuelo. Refactoré la solicitud para enviar lo que estaba enviando en los encabezados ya que el cuerpo de la solicitud y las solicitudes de OPTIONS dejaron de enviarse.
Andre
21
application/xmlo application/jsonno son "encabezados HTTP personalizados". El encabezado en sí sería Content-Typey llamar a ese encabezado "personalizado" sería engañoso.
Leo Correa
1
¡Se eliminaron los encabezados HTTP personalizados y esto funcionó de maravilla!
Tim D
47

Cuando tiene la consola de depuración abierta y la Disable Cacheopción activada, las solicitudes de verificación previa siempre se enviarán (es decir, antes de cada solicitud). Si no deshabilita el caché, se enviará una solicitud previa al vuelo solo una vez (por servidor)

Nir
fuente
3
oh que estoy pensando depurar durante horas esta fue mi solución. caché deshabilitado debido a la consola de depuración.
mauris
1
Incluso si la consola de depuración está cerrada, se envían solicitudes de verificación previa
Luca Perico
2
Luca: eso es cierto, pero el punto es que "deshabilitar caché" no tiene efecto cuando las herramientas de desarrollo están cerradas. las solicitudes de verificación previa se envían solo una vez (por servidor, por supuesto) si la memoria caché no está deshabilitada y se envían antes de cada solicitud si la memoria caché está deshabilitada.
Nir
Eso fue realmente útil.
Anuraag Patil
38

Sí, es posible evitar la solicitud de opciones. La solicitud de opciones es una solicitud de verificación previa cuando envía (publica) cualquier dato a otro dominio. Es un problema de seguridad del navegador. Pero podemos usar otra tecnología: capa de transporte de iframe. Le recomiendo que se olvide de cualquier configuración de CORS y use una solución preparada y funcionará en cualquier lugar.

Echa un vistazo aquí: https://github.com/jpillora/xdomain

Y ejemplo de trabajo: http://jpillora.com/xdomain/

XTRUST.ORG
fuente
¿Es esto realmente una especie de proxy directo?
matanster
15
"La solicitud de opciones es una solicitud de verificación previa cuando envía (publica) cualquier dato a otro dominio". - Eso no es cierto. Puede usar XHR para enviar cualquier solicitud POST que pueda enviar con un formulario HTML normal sin activar una solicitud de verificación previa. Solo cuando comienza a hacer cosas que un formulario no puede hacer (como tipos de contenido personalizados o encabezados de solicitud adicionales) se envía una verificación previa.
Quentin
La solución aquí parece depender de una cuña de iframe que funciona en algunos casos, pero tiene algunas limitaciones importantes. ¿Qué sucede si desea conocer el código de estado HTTP de la respuesta o el valor de otro encabezado de respuesta HTTP?
Stephen Crosby
55
iFrames no fueron hechos para eso.
Romko
1
esta es una implementación de software y responde a la pregunta final que fue: "¿Hay alguna forma de evitar que el navegador envíe solicitudes de OPCIONES por completo?". En pocas palabras, no hay forma de deshabilitarlo en Mozilla o Chromium, está implementado en el código y las únicas opciones de "trabajo" simplemente eluden el comportamiento.
carroñero
15

Para un desarrollador que comprende la razón por la que existe pero necesita acceder a una API que no maneja llamadas OPTIONS sin autenticación, necesito una respuesta temporal para poder desarrollar localmente hasta que el propietario de la API agregue el soporte SPA CORS adecuado u obtenga una API proxy está funcionando.

Descubrí que puedes deshabilitar CORS en Safari y Chrome en una Mac.

Deshabilitar la misma política de origen en Chrome

Chrome: Salga de Chrome, abra una terminal y pegue este comando: open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir

Safari: deshabilitar la política del mismo origen en Safari

Si desea deshabilitar la política del mismo origen en Safari (tengo 9.1.1), solo necesita habilitar el menú de desarrollador y seleccionar "Deshabilitar restricciones de origen cruzado" en el menú de desarrollo.

Joseph Juhnke
fuente
44
Debería poner más énfasis en la parte que dice "¡Esto NUNCA debería ser una solución permanente!" . La misma política de origen es una medida de seguridad del navegador muy importante y nunca debe deshabilitarse cuando normalmente navega por Internet.
Jannis
Desearía que la web funcionara de esta manera, para que podamos solicitar los datos que necesitamos de los servidores sin problemas adicionales.
jemiloii
14

Como ya se mencionó en publicaciones anteriores, las OPTIONSsolicitudes están ahí por una razón. Si tiene un problema con grandes tiempos de respuesta de su servidor (por ejemplo, conexión en el extranjero) también puede hacer que su navegador guarde en caché las solicitudes de verificación previa.

Haga que su servidor responda con el Access-Control-Max-Ageencabezado y para las solicitudes que van al mismo punto final, la solicitud de verificación previa se almacenará en caché y ya no se producirá.

enpenax
fuente
1
¡Gracias por esto! El hecho de que las OPTIONSsolicitudes se almacenen en caché con este encabezado es bastante opaco en toda la documentación de CORS que he leído.
joshperry
Y el caché solo tiene efecto con la misma URL exacta. Quiero un caché de verificación previa de nivel de dominio que realmente puede reducir los viajes de ida y vuelta. (CORS es tonto!)
pregunto
8

He resuelto este problema como.

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();
}

Es solo para el desarrollo. Con esto espero 9ms y 500ms y no 8s y 500ms. Puedo hacerlo porque la aplicación JS de producción estará en la misma máquina que la producción, por lo que no habrá, OPTIONSpero el desarrollo es mi local.

AntiCZ
fuente
4

No puedes pero puedes evitar CORS usando JSONP.

Jose Mato
fuente
2
Solo recibe una solicitud de OPCIONES si está haciendo algo no simple . Solo puede realizar solicitudes simples (GET, sin encabezados personalizados, sin datos de autenticación) con JSONP, por lo que JSONP no puede sustituir aquí.
Quentin
Sí, lo sé pero no sé exactamente los requisitos del proyecto. Sé que no es simple evitar los cors, pero depende del proyecto. En el peor de los casos para evitar CORS, debe pasar datos utilizando parámetros get. Entonces, JSONP podría reemplazar los cors dependiendo de los requisitos del proyecto (como dijiste, usando solicitudes simples)
Jose Mato
No entiendo el diseño del llamado pre-vuelo. ¿Qué podría hacer que no sea seguro si el cliente decide enviar datos al servidor? No veo que tenga sentido duplicar la carga en el cable.
Qian Chen
@ElgsQianChen esto probablemente pueda responder a su pregunta stackoverflow.com/questions/15381105/…
Leo Correa
0

Después de pasar todo un día y medio tratando de resolver un problema similar, descubrí que tenía que ver con IIS .

Mi proyecto de API web se configuró de la siguiente manera:

// WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
    var cors = new EnableCorsAttribute("*", "*", "*");
    config.EnableCors(cors);
    //...
}

No tenía opciones de configuración específicas de CORS en el nodo web.config> system.webServer como he visto en tantas publicaciones

No hay código específico de CORS en global.asax o en el controlador como decorador

El problema era la configuración del grupo de aplicaciones .

El modo de canalización administrado se configuró en clásico (lo cambió a integrado ) y la Identidad se configuró en Servicio de red (lo cambió a ApplicationPoolIdentity )

Cambiar esa configuración (y actualizar el grupo de aplicaciones) me lo arregló.

Ju66ernaut
fuente
-2

Lo que funcionó para mí fue importar "github.com/gorilla/handlers" y luego usarlo de esta manera:

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)))

Tan pronto como ejecuté una solicitud POST de Ajax y le adjunté datos JSON, Chrome siempre agregaba el encabezado Content-Type que no estaba en mi configuración anterior de AllowHeaders.

Kurt
fuente
-2

Una solución que he usado en el pasado: digamos que su sitio está en mydomain.com y necesita hacer una solicitud ajax a foreigndomain.com

Configure una reescritura de IIS de su dominio al dominio externo, p. Ej.

<rewrite>
  <rules>
    <rule name="ForeignRewrite" stopProcessing="true">
        <match url="^api/v1/(.*)$" />
        <action type="Rewrite" url="https://foreigndomain.com/{R:1}" />
    </rule>
  </rules>
</rewrite>

en su sitio de mydomain.com: puede hacer una misma solicitud de origen, y no hay necesidad de ninguna solicitud de opciones :)

David McEleney
fuente
-2

Se puede resolver en caso de uso de un proxy que intercepte la solicitud y escriba los encabezados apropiados. En el caso particular de Varnish, estas serían las reglas:

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;
}

}

Rafa Cuestas
fuente
-5

Tal vez haya una solución (pero no la probé): podría usar CSP (Política de seguridad de contenido) para habilitar su dominio remoto y los navegadores tal vez omitan la verificación de solicitud de OPCIONES DE CORS.

Si encuentro algo de tiempo, lo probaré y actualizaré esta publicación.

CSP: https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Content-Security-Policy

Especificación de CSP: https://www.w3.org/TR/CSP/

Arnaud Tournier
fuente
Acabo de probar y no funciona, CORS todavía se requiere después de CSP para la admisión de la solicitud xhr ...
Arnaud Tournier