Pourquoi une demande OPTIONS est-elle envoyée et puis-je la désactiver?

416

Je construis une API web. J'ai trouvé que chaque fois que j'utilise Chrome pour POST, GET pour mon API, il y a toujours une demande OPTIONS envoyée avant la vraie demande, ce qui est assez ennuyeux. Actuellement, je demande au serveur d'ignorer toutes les demandes OPTIONS. Maintenant, ma question est de savoir ce qui est bon d'envoyer une demande OPTIONS pour doubler la charge du serveur? Existe-t-il un moyen d'empêcher complètement le navigateur d'envoyer des requêtes OPTIONS?

Qian Chen
la source

Réponses:

376

edit 2018-09-13 : ajout de quelques précisions sur cette demande de pré-vol et comment l'éviter à la fin de cette réponse.

OPTIONSles demandes sont ce que nous appelons des pre-flightdemandes Cross-origin resource sharing (CORS).

Ils sont nécessaires lorsque vous effectuez des demandes d'origines différentes dans des situations spécifiques.

Cette demande de pré-vol est effectuée par certains navigateurs par mesure de sécurité pour garantir que la demande en cours est approuvée par le serveur. Cela signifie que le serveur comprend que la méthode, l'origine et les en-têtes envoyés sur la demande sont sûrs d'agir.

Votre serveur ne doit pas ignorer mais gérer ces demandes chaque fois que vous essayez de faire des demandes d'origine croisée.

Une bonne ressource peut être trouvée ici http://enable-cors.org/

Un moyen de les gérer pour être à l'aise est de s'assurer que pour tout chemin avec OPTIONSméthode, le serveur envoie une réponse avec cet en-tête

Access-Control-Allow-Origin: *

Cela indiquera au navigateur que le serveur est prêt à répondre aux demandes de toute origine.

Pour plus d'informations sur la façon d'ajouter la prise en charge CORS à votre serveur, voir l'organigramme suivant

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

Organigramme CORS


modifier 2018-09-13

La OPTIONSdemande CORS n'est déclenchée que dans certains cas, comme expliqué dans les documents MDN :

Certaines demandes ne déclenchent pas de contrôle en amont CORS. Celles-ci sont appelées «demandes simples» dans cet article, bien que la spécification Fetch (qui définit CORS) n'utilise pas ce terme. Une demande qui ne déclenche pas un contrôle en amont CORS - une soi-disant «demande simple» - est celle qui remplit toutes les conditions suivantes:

Les seules méthodes autorisées sont:

  • AVOIR
  • TÊTE
  • PUBLIER

Hormis les en-têtes définis automatiquement par l'agent utilisateur (par exemple, Connection, User-Agent ou tout autre en-tête dont le nom est défini dans la spécification Fetch comme «nom d'en-tête interdit»), les seuls en-têtes autorisés à être définies manuellement sont celles que la spécification Fetch définit comme étant un «en-tête de demande sécurisé CORS», qui sont:

  • J'accepte
  • Accept-Language
  • Langue du contenu
  • Type de contenu (mais notez les exigences supplémentaires ci-dessous)
  • DPR
  • Liaison descendante
  • Enregistrer des données
  • Largeur de la fenêtre
  • Largeur

Les seules valeurs autorisées pour l'en-tête Content-Type sont:

  • application / x-www-form-urlencoded
  • multipart / form-data
  • texte simple

Aucun écouteur d'événement n'est enregistré sur aucun objet XMLHttpRequestUpload utilisé dans la demande; ceux-ci sont accessibles à l'aide de la propriété XMLHttpRequest.upload.

Aucun objet ReadableStream n'est utilisé dans la demande.

Leo Correa
la source
8
Mais il n'est pas réaliste de définir ce drapeau Chrome pour tous les utilisateurs généraux.
Qian Chen
37
Il est incorrect de dire que des demandes de contrôle en amont sont requises lors de la création de demandes d'origine croisée. Les demandes de contrôle en amont ne sont nécessaires que dans des situations spécifiques, comme si vous définissez des en-têtes personnalisés ou si vous effectuez des demandes autres que get, head et post.
Robin Clowers
4
Curieusement, lorsque vous faites un CORS demander l' aide jQuery, la bibliothèque JavaScript évite la mise en en particulier l' en- tête personnalisé, avec un mot d'avertissement aux développeurs: Pour les demandes inter-domaines, car les conditions d'un contrôle en amont sont semblables à un puzzle, nous il suffit de ne jamais le régler pour être sûr.
Sous le radar
3
Comment se fait-il que si je fais un curlapi, cela fonctionne, mais lors de l'exécution de Chrome, j'obtiens l'erreur?
SuperUberDuper
5
@SuperUberDuper parce que les demandes CORS et de contrôle en amont sont liées au navigateur. Vous pouvez simuler CORS en ajoutant un en- Origintête à votre demande pour simuler comme si la demande provenait d'un hôte spécifique (par exemple, votre site Web.com). Vous pouvez également simuler des demandes de contrôle en amont en définissant la méthode HTTP d'une demande OPTIONSet les en- Access-Control-*têtes
Leo Correa
234

J'ai traversé ce problème, voici ma conclusion à ce problème et ma solution.

Selon la stratégie CORS (je vous recommande fortement de lire à ce sujet) Vous ne pouvez pas simplement forcer le navigateur à arrêter d'envoyer une demande OPTIONS s'il le pense.

Vous pouvez contourner ce problème de deux manières:

  1. Assurez-vous que votre demande est une "simple demande"
  2. Défini Access-Control-Max-Agepour la demande OPTIONS

Demande simple

Une simple demande intersite répond à toutes les conditions suivantes:

Les seules méthodes autorisées sont:

  • AVOIR
  • TÊTE
  • PUBLIER

Hormis les en-têtes définis automatiquement par l'agent utilisateur (par exemple, Connection, User-Agent, etc.), les seuls en-têtes qui peuvent être définis manuellement sont:

  • J'accepte
  • Accept-Language
  • Langue du contenu
  • Type de contenu

Les seules valeurs autorisées pour l'en-tête Content-Type sont:

  • application / x-www-form-urlencoded
  • multipart / form-data
  • texte simple

Une simple demande n'entraînera pas de demande d'OPTIONS avant le vol.

Définir un cache pour la vérification OPTIONS

Vous pouvez définir un Access-Control-Max-Agepour la demande OPTIONS, afin qu'il ne vérifie pas à nouveau l'autorisation jusqu'à son expiration.

Access-Control-Max-Age donne la valeur en secondes pour combien de temps la réponse à la demande de contrôle en amont peut être mise en cache sans envoyer une autre demande de contrôle en amont.

Limitation notée

  • Pour Chrome, les secondes maximum pour Access-Control-Max-Ageest -ce 600qui est de 10 minutes, en fonction de chrome code source
  • Access-Control-Max-Agene fonctionne que pour une ressource à chaque fois, par exemple, les GETdemandes avec le même chemin URL mais différentes requêtes seront traitées comme des ressources différentes. Ainsi, la demande adressée à la deuxième ressource déclenchera toujours une demande de contrôle en amont.
Neekey
la source
3
Oui ... cela devrait être la réponse acceptée et la plus pertinente pour la question ..!
Rajesh Mbm
7
Merci d'avoir mentionné Access-Control-Max-Age. Voilà la clé ici. Il vous aide à éviter les demandes de contrôle en amont excessives.
Idris Mokhtarzada
J'utilise axios pour appeler get request. Où puis-je définir Access-Control-Max-Age dans la demande axios?
Mohit Shah
+1 L'en-tête Access-Control-Max-Age est la clé ici. Cela devrait être la réponse acceptée! J'ai configuré 86400 secondes (24 heures) sur l'en-tête et la demande de prefligth a disparu!
revobtz
1
@VitalyZdanevich non! N'évitez pas application/jsonsimplement parce que cela rend votre demande non "simple" (et déclenche donc CORS). Le navigateur fait son travail. Configurez votre serveur pour renvoyer quelque chose comme un en-tête Access-Control-Max-Age: 86400et le navigateur ne renverra pas de demande OPTIONS pendant 24 heures.
colm.anseo
139

Veuillez vous référer à cette réponse sur le besoin réel d'une demande OPTIONS pré-volée: CORS - Quelle est la motivation derrière l'introduction des demandes de contrôle en amont?

Pour désactiver la demande OPTIONS, les conditions ci-dessous doivent être remplies pour la demande ajax:

  1. La demande ne définit pas d'en-têtes HTTP personnalisés comme «application / xml» ou «application / json», etc.
  2. La méthode de demande doit être GET, HEAD ou POST. Si le POST, le type de contenu devrait être l' une application/x-www-form-urlencoded, multipart/form-dataoutext/plain

Référence: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

device_exec
la source
14
+1 pour les "en-têtes HTTP personnalisés"! Dans mon cas, ils provoquaient le déclenchement de la demande de pré-vol. J'ai refactorisé la demande d'envoi de tout ce que j'envoyais dans les en-têtes car le corps de la demande et les demandes OPTIONS ont cessé d'être envoyés.
Andre
21
application/xmlou application/jsonne sont pas des "en-têtes HTTP personnalisés". L'en-tête lui-même serait Content-Typeet appeler cet en-tête "personnalisé" serait trompeur.
Leo Correa
1
Suppression des en-têtes HTTP personnalisés et cela a fonctionné comme un charme!
Tim D
47

Lorsque la console de débogage est ouverte et que l' Disable Cacheoption est activée, les demandes de contrôle en amont sont toujours envoyées (c'est-à-dire avant chaque demande). si vous ne désactivez pas le cache, une demande de pré-vol ne sera envoyée qu'une seule fois (par serveur)

Nir
la source
3
oh à quoi je pense. après avoir débogué pendant des heures, c'était ma solution. cache désactivé en raison de la console de débogage.
mauris
1
Même si la console de débogage est fermée, des demandes de contrôle en amont sont envoyées
Luca Perico
2
Luca: c'est vrai mais le fait est que le "désactiver le cache" n'a aucun effet lorsque les outils de développement sont fermés. les demandes de contrôle en amont sont envoyées une seule fois (par serveur bien sûr) si le cache n'est pas désactivé et sont envoyées avant chaque demande si le cache est désactivé.
Nir
C'était vraiment utile.
Anuraag Patil
38

Oui, il est possible d'éviter la demande d'options. La demande d'options est une demande de contrôle en amont lorsque vous envoyez (publiez) des données vers un autre domaine. C'est un problème de sécurité du navigateur. Mais nous pouvons utiliser une autre technologie: la couche de transport iframe. Je vous recommande fortement d'oublier toute configuration CORS et d'utiliser une solution prête à l'emploi et cela fonctionnera n'importe où.

Jetez un œil ici: https://github.com/jpillora/xdomain

Et exemple de travail: http://jpillora.com/xdomain/

XTRUST.ORG
la source
est-ce en fait une sorte de proxy sans rendez-vous?
matanster
15
"La demande d'options est une demande de contrôle en amont lorsque vous envoyez (publiez) des données vers un autre domaine." - Ce n'est pas vrai. Vous pouvez utiliser XHR pour envoyer toute demande POST que vous pourriez envoyer avec un formulaire HTML normal sans déclencher une demande de contrôle en amont. Ce n'est que lorsque vous commencez à faire des choses qu'un formulaire ne peut pas faire (comme des types de contenu personnalisés ou des en-têtes de demande supplémentaires) qu'un contrôle en amont est envoyé.
Quentin
La solution ici semble s'appuyer sur un module d'interface iframe qui fonctionne dans certains cas, mais présente certaines limitations majeures. Que se passe-t-il si vous souhaitez connaître le code d'état HTTP de la réponse ou la valeur d'un autre en-tête de réponse HTTP?
Stephen Crosby
5
Les iFrames n'ont pas été conçues pour cela.
Romko
1
il s'agit d'une implémentation logicielle qui répond à la dernière question qui était: "Existe-t-il un moyen d'empêcher complètement le navigateur d'envoyer des requêtes OPTIONS?". Pour faire court, il n'y a aucun moyen de le désactiver dans Mozilla ou Chromium, il est implémenté dans le code et les seules options "de travail" contournent simplement le comportement.
scavenger
15

Pour un développeur qui comprend la raison de son existence mais doit accéder à une API qui ne gère pas les appels OPTIONS sans authentification, j'ai besoin d'une réponse temporaire pour pouvoir développer localement jusqu'à ce que le propriétaire de l'API ajoute le support SPA CORS approprié ou que j'obtienne une API proxy opérationnel.

J'ai trouvé que vous pouvez désactiver CORS dans Safari et Chrome sur un Mac.

Désactiver la même politique d'origine dans Chrome

Chrome: Quittez Chrome, ouvrez un terminal et collez cette commande: open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir

Safari: désactivation de la politique de même origine dans Safari

Si vous souhaitez désactiver la politique de même origine sur Safari (j'ai 9.1.1), il vous suffit d'activer le menu développeur et de sélectionner "Désactiver les restrictions d'origine croisée" dans le menu de développement.

Joseph Juhnke
la source
4
Vous devriez mettre plus de lumière sur la partie qui dit "cela ne devrait JAMAIS être une solution permanente !!!!" . La même politique d'origine est une mesure de sécurité du navigateur très importante et ne doit jamais être désactivée lorsque vous naviguez normalement sur Internet.
jannis
Je souhaite que le Web fonctionne simplement de cette façon, afin que nous puissions simplement demander les données dont nous avons besoin aux serveurs sans tracas supplémentaires.
jemiloii
14

Comme déjà mentionné dans les articles précédents, les OPTIONSdemandes sont là pour une raison. Si vous rencontrez un problème avec les temps de réponse importants de votre serveur (par exemple, une connexion à l'étranger), vous pouvez également demander à votre navigateur de mettre en cache les demandes de contrôle en amont.

Demandez à votre serveur de répondre avec l'en- Access-Control-Max-Agetête et pour les demandes qui vont au même point de terminaison, la demande de contrôle en amont aura été mise en cache et ne se produira plus.

enpenax
la source
1
Merci pour ça! Le fait que les OPTIONSrequêtes soient mises en cache avec cet en-tête est assez opaque dans toute la documentation CORS que j'ai lue.
joshperry
Et le cache ne prend effet qu'avec la même URL exacte. Je veux un cache de contrôle en amont au niveau du domaine qui peut vraiment réduire les allers-retours. (CORS est idiot!)
merveille
8

J'ai résolu ce problème comme.

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

C'est uniquement pour le développement. Avec cela j'attends 9ms et 500ms et non 8s et 500ms. Je peux le faire parce que l'application JS de production sera sur la même machine que la production, donc il n'y en aura pas OPTIONSmais le développement est mon local.

AntiCZ
la source
4

Vous ne pouvez pas mais vous pouvez éviter CORS en utilisant JSONP.

Jose Mato
la source
2
Vous ne recevez une demande OPTIONS que si vous faites quelque chose de pas simple . Vous ne pouvez faire que des demandes simples (GET, pas d'en-têtes personnalisés, pas de données d'authentification) avec JSONP, donc JSONP ne peut pas se substituer ici.
Quentin
Oui, je le sais, mais je ne connais pas exactement les exigences du projet. Je sais que ce n'est pas simple d'éviter les cors mais cela dépend du projet. Dans le pire des cas, pour éviter CORS, vous devez transmettre des données à l'aide des paramètres get. Ainsi, JSONP pourrait remplacer les cors en fonction des exigences du projet (comme vous l'avez dit, en utilisant de simples demandes)
Jose Mato
Je ne comprends pas la conception de ce qu'on appelle le pré-vol. Qu'est-ce qui pourrait être dangereux si le client décidait d'envoyer des données au serveur? Je ne pense pas qu'il soit logique de doubler la charge sur le fil.
Qian Chen
@ElgsQianChen, cela peut probablement répondre à votre question stackoverflow.com/questions/15381105/…
Leo Correa
0

Après avoir passé une journée et demie à essayer de résoudre un problème similaire, j'ai trouvé que cela avait à voir avec IIS .

Mon projet d'API Web a été configuré comme suit:

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

Je n'avais pas d'options de configuration spécifiques à CORS dans le nœud web.config> system.webServer comme je l'ai vu dans tant de messages

Pas de code spécifique CORS dans le global.asax ou dans le contrôleur en tant que décorateur

Le problème était les paramètres du pool d'applications .

Le mode de pipeline géré a été défini sur classique (l'a changé en intégré ) et l' identité a été définie sur Service réseau (l'a changé sur ApplicationPoolIdentity )

La modification de ces paramètres (et l'actualisation du pool d'applications) l'a corrigé pour moi.

Ju66ernaut
la source
-2

Ce qui a fonctionné pour moi, c'était d'importer "github.com/gorilla/handlers", puis de l'utiliser de cette façon:

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

Dès que j'exécute une demande Ajax POST et que je lui attache des données JSON, Chrome ajoute toujours l'en-tête Content-Type qui ne figurait pas dans ma précédente configuration AllowedHeaders.

Kurt
la source
-2

Une solution que j'ai utilisée dans le passé - disons que votre site se trouve sur mydomain.com et que vous devez faire une demande ajax à foreigndomain.com

Configurez une réécriture IIS de votre domaine vers le domaine étranger - par exemple

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

sur votre site mydomain.com - vous pouvez alors faire une même demande d'origine, et aucune demande d'options n'est nécessaire :)

David McEleney
la source
-2

Il peut être résolu en cas d'utilisation d'un proxy qui intercepte la demande et écrit les en-têtes appropriés. Dans le cas particulier du vernis, ce seraient les règles:

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
la source
-5

Il existe peut-être une solution (mais je ne l'ai pas testée): vous pouvez utiliser CSP (Content Security Policy) pour activer votre domaine distant et les navigateurs ignoreront peut-être la vérification de la demande CORS OPTIONS.

Si je trouve un peu de temps, je vais tester cela et mettre à jour ce post!

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

Spécification CSP: https://www.w3.org/TR/CSP/

Arnaud Tournier
la source
Je viens de tester et ça ne marche pas, CORS est toujours requis après CSP pour l'admission de la demande xhr ...
Arnaud Tournier