Warum wird eine OPTIONS-Anfrage gesendet und kann ich sie deaktivieren?

414

Ich erstelle eine Web-API. Ich habe festgestellt, dass immer, wenn ich Chrome zum POSTEN und GET zu meiner API verwende, vor der eigentlichen Anfrage eine OPTIONS-Anfrage gesendet wird, was ziemlich ärgerlich ist. Derzeit lasse ich den Server alle OPTIONS-Anfragen ignorieren. Meine Fragen sind nun, was ist gut, um eine OPTIONS-Anfrage zu senden, um die Serverlast zu verdoppeln? Gibt es eine Möglichkeit, den Browser vollständig daran zu hindern, OPTIONS-Anfragen zu senden?

Qian Chen
quelle

Antworten:

375

edit 2018-09-13 : Einige Präzisionen zu dieser Anfrage vor dem Flug hinzugefügt und wie sie am Ende dieser Antwort vermieden werden kann.

OPTIONSAnfragen sind das, was wir pre-flightAnfragen nennen Cross-origin resource sharing (CORS).

Sie sind erforderlich, wenn Sie in bestimmten Situationen Anfragen unterschiedlicher Herkunft stellen.

Diese Anforderung vor dem Flug wird von einigen Browsern aus Sicherheitsgründen gestellt, um sicherzustellen, dass der Server der ausgeführten Anforderung vertraut. Dies bedeutet, dass der Server versteht, dass die Methode, der Ursprung und die Header, die auf die Anforderung gesendet werden, sicher sind.

Ihr Server sollte diese Anforderungen nicht ignorieren, sondern verarbeiten, wenn Sie versuchen, Ursprungsübergreifende Anforderungen auszuführen.

Eine gute Ressource finden Sie hier http://enable-cors.org/

Eine Möglichkeit, dies zu handhaben, besteht darin, sicherzustellen, dass OPTIONSder Server für jeden Pfad mit Methode eine Antwort mit diesem Header sendet

Access-Control-Allow-Origin: *

Dadurch wird dem Browser mitgeteilt, dass der Server bereit ist, Anfragen von jedem Ursprung zu beantworten.

Weitere Informationen zum Hinzufügen von CORS-Unterstützung zu Ihrem Server finden Sie im folgenden Flussdiagramm

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

CORS Flussdiagramm


bearbeiten 2018-09-13

Die CORS- OPTIONSAnforderung wird nur in einigen Fällen ausgelöst, wie in den MDN-Dokumenten erläutert :

Einige Anfragen lösen keinen CORS-Preflight aus. Diese werden in diesem Artikel als "einfache Anforderungen" bezeichnet, obwohl die Fetch-Spezifikation (die CORS definiert) diesen Begriff nicht verwendet. Eine Anfrage, die keinen CORS-Preflight auslöst - eine sogenannte „einfache Anfrage“ - erfüllt alle folgenden Bedingungen:

Die einzigen zulässigen Methoden sind:

  • ERHALTEN
  • KOPF
  • POST

Abgesehen von den vom Benutzeragenten automatisch festgelegten Headern (z. B. Verbindung, Benutzeragent oder einem der anderen Header mit Namen, die in der Abrufspezifikation als „verbotener Headername“ definiert sind), sind dies die einzigen zulässigen Header manuell festgelegt sind diejenigen, die in der Fetch-Spezifikation als "CORS-sicherer Listen-Anforderungsheader" definiert sind:

  • Akzeptieren
  • Akzeptiere-Sprache
  • Inhaltssprache
  • Inhaltstyp (beachten Sie jedoch die zusätzlichen Anforderungen unten)
  • DPR
  • Downlink
  • Daten speichern
  • Ansichtsfenster-Breite
  • Breite

Die einzigen zulässigen Werte für den Content-Type-Header sind:

  • application / x-www-form-urlencoded
  • mehrteilige / Formulardaten
  • Text / Klartext

Für kein in der Anforderung verwendetes XMLHttpRequestUpload-Objekt sind Ereignis-Listener registriert. Auf diese wird mit der Eigenschaft XMLHttpRequest.upload zugegriffen.

In der Anforderung wird kein ReadableStream-Objekt verwendet.

Leo Correa
quelle
8
Es ist jedoch nicht realistisch, dieses Chrome-Flag für alle allgemeinen Benutzer zu setzen.
Qian Chen
37
Es ist falsch zu sagen, dass Preflight-Anfragen erforderlich sind, wenn Cross-Origin-Anfragen gestellt werden. Preflight-Anforderungen sind nur in bestimmten Situationen erforderlich, z. B. wenn Sie benutzerdefinierte Header festlegen oder andere Anforderungen als get, head und post stellen.
Robin Clowers
4
Witzigerweise vermeidet die JavaScript-Bibliothek beim Erstellen einer CORS-Anfrage mit jQuery ausdrücklich das Festlegen des benutzerdefinierten Headers sowie eine Warnung an die Entwickler: Bei domänenübergreifenden Anfragen ähneln die Bedingungen für einen Preflight einem Puzzle Stellen Sie es einfach nie sicher ein.
Unter dem Radar
3
Wie kommt es, wenn ich eine curlAPI mache , die funktioniert, aber wenn ich von Chrome aus laufe, bekomme ich den Fehler?
SuperUberDuper
5
@SuperUberDuper, da CORS- und Preflight-Anfragen eine browserbezogene Angelegenheit sind. Sie können CORS simulieren, indem Sie OriginIhrer Anfrage einen Header hinzufügen , um zu simulieren, als ob die Anfrage von einem bestimmten Host (z. B. yourwebsite.com) stammt. Sie können auch Preflight-Anforderungen simulieren, indem Sie die HTTP-Methode einer Anforderung auf OPTIONSund die Access-Control-*Header
Leo Correa
234

Ich habe dieses Problem durchgearbeitet. Nachfolgend finden Sie meine Schlussfolgerung zu diesem Problem und meine Lösung.

Gemäß der CORS-Strategie (empfehlen dringend, darüber zu lesen) können Sie den Browser nicht einfach zwingen, das Senden von OPTIONS-Anforderungen zu beenden, wenn er dies für erforderlich hält.

Es gibt zwei Möglichkeiten, wie Sie das umgehen können:

  1. Stellen Sie sicher, dass Ihre Anfrage eine "einfache Anfrage" ist.
  2. Stellen Sie Access-Control-Max-Agefür die OPTIONS - Anforderung

Einfache Anfrage

Eine einfache standortübergreifende Anforderung erfüllt alle folgenden Bedingungen:

Die einzigen zulässigen Methoden sind:

  • ERHALTEN
  • KOPF
  • POST

Abgesehen von den vom Benutzeragenten automatisch festgelegten Headern (z. B. Verbindung, Benutzeragent usw.) können nur folgende Header manuell festgelegt werden:

  • Akzeptieren
  • Akzeptiere-Sprache
  • Inhaltssprache
  • Inhaltstyp

Die einzigen zulässigen Werte für den Content-Type-Header sind:

  • application / x-www-form-urlencoded
  • mehrteilige / Formulardaten
  • Text / Klartext

Eine einfache Anfrage führt nicht zu einer OPTIONS-Anfrage vor dem Flug.

Legen Sie einen Cache für die OPTIONS-Prüfung fest

Sie können eine Access-Control-Max-Agefür die OPTIONS-Anforderung festlegen, damit die Berechtigung erst nach Ablauf erneut überprüft wird.

Access-Control-Max-Age gibt den Wert in Sekunden an, wie lange die Antwort auf die Preflight-Anforderung zwischengespeichert werden kann, ohne eine weitere Preflight-Anforderung zu senden.

Einschränkung notiert

  • Für Chrome Access-Control-Max-Agebeträgt 600die maximale Sekunde laut Chrome-Quellcode 10 Minuten
  • Access-Control-Max-AgeFunktioniert jedes Mal nur für eine Ressource, z. B. GETAnforderungen mit demselben URL-Pfad, aber unterschiedliche Abfragen werden als unterschiedliche Ressourcen behandelt. Die Anforderung an die zweite Ressource löst also weiterhin eine Preflight-Anforderung aus.
Neekey
quelle
3
Ja ... dies sollte die akzeptierte Antwort sein und für die Frage am relevantesten sein ..!
Rajesh Mbm
7
Vielen Dank für die Erwähnung Access-Control-Max-Age. Das ist der Schlüssel hier. Es hilft Ihnen, übermäßige Preflight-Anfragen zu vermeiden.
Idris Mokhtarzada
Ich benutze Axios, um get request anzurufen. Wo kann ich Access-Control-Max-Age in Axios-Anfrage einstellen?
Mohit Shah
+1 Der Header Access-Control-Max-Age ist hier der Schlüssel. Dies sollte die akzeptierte Antwort sein! Ich habe 86400 Sekunden (24 Stunden) im Header eingerichtet und die Prefligth-Anfrage ist weg!
Revobtz
1
@VitalyZdanevich nein! Vermeiden Sie es nicht, application/jsonnur weil es Ihre Anfrage nicht "einfach" macht (und somit CORS auslöst). Der Browser macht seine Arbeit. Stellen Sie Ihren Server so ein, dass er einen Header Access-Control-Max-Age: 86400zurückgibt, und der Browser sendet eine OPTIONS-Anfrage 24 Stunden lang nicht erneut.
colm.anseo
139

Bitte beziehen Sie sich auf diese Antwort zum tatsächlichen Bedarf an vorgeflogenen OPTIONS-Anfragen: CORS - Was ist die Motivation für die Einführung von Preflight-Anfragen?

Um die OPTIONS-Anforderung zu deaktivieren, müssen die folgenden Bedingungen für eine Ajax-Anforderung erfüllt sein:

  1. Request setzt keine benutzerdefinierten HTTP-Header wie 'application / xml' oder 'application / json' usw.
  2. Die Anforderungsmethode muss GET, HEAD oder POST sein. Wenn POST sollte Inhaltstyp einer der folgenden sein application/x-www-form-urlencoded, multipart/form-dataodertext/plain

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

device_exec
quelle
14
+1 für "benutzerdefinierte HTTP-Header"! In meinem Fall wurde die Vorfluganforderung ausgelöst. Ich habe die Anforderung zum Senden von allem, was ich in den Headern gesendet habe, überarbeitet, da der Anforderungshauptteil und die OPTIONS-Anforderungen nicht mehr gesendet wurden.
Andre
21
application/xmloder application/jsonsind nicht "Benutzerdefinierte HTTP-Header". Der Header selbst wäre Content-Typeund es wäre irreführend, diesen Header "custom" zu nennen.
Leo Correa
1
Benutzerdefinierte HTTP-Header wurden entfernt und dies funktionierte wie ein Zauber!
Tim D
47

Wenn Sie die Debug-Konsole geöffnet und die Disable CacheOption aktiviert haben, werden Preflight-Anforderungen immer gesendet (dh vor jeder einzelnen Anforderung). Wenn Sie den Cache nicht deaktivieren, wird eine Anfrage vor dem Flug nur einmal gesendet (pro Server).

Nir
quelle
3
Oh, was denke ich? stundenlanges Debuggen war meine Lösung. Cache wegen Debugging-Konsole deaktiviert.
Mauris
1
Selbst wenn die Debug-Konsole geschlossen ist, werden Preflight-Anfragen gesendet
Luca Perico
2
Luca: Das stimmt, aber der Punkt ist, dass der "Cache deaktivieren" keine Auswirkung hat, wenn die Entwickler-Tools geschlossen sind. Preflight-Anforderungen werden nur einmal gesendet (natürlich pro Server), wenn der Cache nicht deaktiviert ist, und vor jeder einzelnen Anforderung, wenn der Cache deaktiviert ist.
Nir
Das war sehr hilfreich.
Anuraag Patil
38

Ja, es ist möglich, Optionsanfragen zu vermeiden. Die Optionsanforderung ist eine Preflight-Anforderung, wenn Sie Daten an eine andere Domain senden (veröffentlichen). Es ist ein Browser-Sicherheitsproblem. Wir können jedoch eine andere Technologie verwenden: die Iframe-Transportschicht. Ich empfehle Ihnen dringend, jede CORS-Konfiguration zu vergessen und eine vorgefertigte Lösung zu verwenden, die überall funktioniert.

Schauen Sie hier: https://github.com/jpillora/xdomain

Und Arbeitsbeispiel: http://jpillora.com/xdomain/

XTRUST.ORG
quelle
Ist dies tatsächlich eine Art Drop-In-Proxy?
Matanster
15
"Optionsanforderung ist eine Preflight-Anforderung, wenn Sie Daten an eine andere Domain senden (senden)." - Das ist nicht wahr. Sie können XHR verwenden, um jede POST-Anfrage zu senden, die Sie mit einem normalen HTML-Formular senden können, ohne eine Preflight-Anfrage auszulösen. Nur wenn Sie anfangen, Dinge zu tun, die ein Formular nicht kann (wie benutzerdefinierte Inhaltstypen oder zusätzliche Anforderungsheader), wird ein Preflight gesendet.
Quentin
Die Lösung hier scheint auf einem Iframe-Shim zu beruhen, der in einigen Fällen funktioniert, jedoch einige wesentliche Einschränkungen aufweist. Was passiert, wenn Sie den HTTP-Statuscode der Antwort oder den Wert eines anderen HTTP-Antwortheaders wissen möchten?
Stephen Crosby
5
iFrames wurden dafür nicht gemacht.
Romko
1
Dies ist eine Software-Implementierung und beantwortet die letzte Frage: "Gibt es eine Möglichkeit, den Browser vollständig daran zu hindern, OPTIONS-Anfragen zu senden?". Kurz gesagt, es gibt keine Möglichkeit, es in Mozilla oder Chromium zu deaktivieren. Es ist im Code implementiert und die einzigen "funktionierenden" Optionen umgehen nur das Verhalten.
Aasfresser
15

Für Entwickler, die den Grund dafür verstehen, aber auf eine API zugreifen müssen, die OPTIONS-Aufrufe ohne Authentifizierung nicht verarbeitet, benötige ich eine vorübergehende Antwort, damit ich lokal entwickeln kann, bis der API-Eigentümer die richtige SPA CORS-Unterstützung hinzufügt oder ich eine Proxy-API erhalte in Betrieb.

Ich habe festgestellt, dass Sie CORS in Safari und Chrome auf einem Mac deaktivieren können.

Deaktivieren Sie dieselbe Ursprungsrichtlinie in Chrome

Chrome: Beenden Sie Chrome, öffnen Sie ein Terminal und fügen Sie diesen Befehl ein: open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir

Safari: Deaktivieren der Richtlinie für denselben Ursprung in Safari

Wenn Sie die Richtlinie mit demselben Ursprung in Safari deaktivieren möchten (ich habe 9.1.1), müssen Sie nur das Entwicklermenü aktivieren und im Entwicklungsmenü die Option "Ursprungsübergreifende Einschränkungen deaktivieren" auswählen.

Joseph Juhnke
quelle
4
Sie sollten den Teil, der sagt "Dies sollte NIEMALS eine dauerhafte Lösung sein !!!!" , stärker hervorheben. . Die gleiche Ursprungsrichtlinie ist eine sehr wichtige Sicherheitsmaßnahme für den Browser und sollte beim normalen Surfen im Internet niemals deaktiviert werden.
Jannis
Ich wünschte, das Web würde einfach so funktionieren, sodass wir einfach Daten, die wir benötigen, ohne zusätzlichen Aufwand von Servern anfordern können.
jemiloii
14

Wie bereits in früheren Beiträgen erwähnt, OPTIONSgibt es Anfragen aus einem bestimmten Grund. Wenn Sie ein Problem mit langen Antwortzeiten von Ihrem Server haben (z. B. eine Verbindung nach Übersee), können Sie die Preflight-Anforderungen auch von Ihrem Browser zwischenspeichern lassen.

Lassen Sie Ihren Server mit dem Access-Control-Max-AgeHeader antworten. Bei Anforderungen, die an denselben Endpunkt gesendet werden, wurde die Preflight-Anforderung zwischengespeichert und tritt nicht mehr auf.

Enpenax
quelle
1
Danke dafür! Die Tatsache, dass OPTIONSAnfragen mit diesem Header zwischengespeichert werden, ist in der gesamten CORS-Dokumentation, die ich gelesen habe, ziemlich undurchsichtig.
Joshperry
Und der Cache wird nur mit genau derselben URL wirksam. Ich möchte einen Preflight-Cache auf Domain-Ebene, der die Roundtrips wirklich reduzieren kann. (CORS ist dumm!)
Wunder
8

Ich habe dieses Problem gerne gelöst.

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 ist nur für die Entwicklung. Damit warte ich 9ms und 500ms und nicht 8s und 500ms. Ich kann das tun, weil sich die JS-Produktions-App auf demselben Computer wie die Produktion befindet, sodass es keine gibt, OPTIONSaber die Entwicklung ist meine lokale.

AntiCZ
quelle
4

Sie können nicht, aber Sie könnten CORS mit JSONP vermeiden.

Jose Mato
quelle
2
Sie erhalten nur dann eine OPTIONS-Anfrage, wenn Sie etwas nicht Einfaches tun . Sie können mit JSONP nur einfache Anforderungen stellen (GET, keine benutzerdefinierten Header, keine Authentifizierungsdaten), sodass JSONP hier nicht ersetzen kann.
Quentin
Ja, das weiß ich, aber ich kenne die genauen Projektanforderungen nicht. Ich weiß, es ist nicht einfach, Cors zu vermeiden, aber es hängt vom Projekt ab. Im schlimmsten Fall müssen Sie Daten mit get-Parametern übergeben, um CORS zu vermeiden. So könnte JSONP cors abhängig von den Projektanforderungen ersetzen (wie Sie sagten, mit einfachen Anfragen)
Jose Mato
Ich verstehe das Design des sogenannten Vorfluges nicht. Was kann dazu führen, dass es unsicher wird, wenn der Client Daten an den Server sendet? Ich sehe keinen Sinn darin, die Belastung des Drahtes zu verdoppeln.
Qian Chen
@ ElgsQianChen dies kann wahrscheinlich Ihre Frage stackoverflow.com/questions/15381105/…
Leo Correa
0

Nachdem ich anderthalb Tage damit verbracht hatte, ein ähnliches Problem zu lösen, stellte ich fest, dass es mit IIS zu tun hatte .

Mein Web-API-Projekt wurde wie folgt eingerichtet:

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

Ich hatte keine CORS-spezifischen Konfigurationsoptionen im Knoten web.config> system.webServer, wie ich sie in so vielen Beiträgen gesehen habe

Kein CORS-spezifischer Code in der global.asax oder im Controller als Dekorateur

Das Problem waren die Einstellungen des App-Pools .

Der verwaltete Pipeline-Modus wurde auf klassisch ( geändert in integriert ) und die Identität auf Netzwerkdienst ( geändert in ApplicationPoolIdentity ) festgelegt.

Durch Ändern dieser Einstellungen (und Aktualisieren des App-Pools) wurde das Problem für mich behoben.

Ju66ernaut
quelle
-2

Für mich hat es funktioniert, "github.com/gorilla/handlers" zu importieren und dann folgendermaßen zu verwenden:

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

Sobald ich eine Ajax-POST-Anforderung ausgeführt und JSON-Daten angehängt habe, fügte Chrome immer den Content-Type-Header hinzu, der nicht in meiner vorherigen AllowedHeaders-Konfiguration enthalten war.

Kurt
quelle
-2

Eine Lösung, die ich in der Vergangenheit verwendet habe: Nehmen wir an, Ihre Website befindet sich auf mydomain.com, und Sie müssen eine Ajax-Anfrage an Foreigndomain.com stellen

Konfigurieren Sie ein IIS-Rewrite von Ihrer Domain in die fremde Domain - z

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

auf Ihrer mydomain.com-Website - Sie können dann dieselbe Ursprungsanfrage stellen, und es ist keine Optionsanfrage erforderlich :)

David McEleney
quelle
-2

Dies kann bei Verwendung eines Proxys gelöst werden, der die Anforderung abfängt und die entsprechenden Header schreibt. Im speziellen Fall von Lack wären dies die Regeln:

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
quelle
-5

Möglicherweise gibt es eine Lösung (aber ich habe sie nicht getestet): Sie können CSP (Content Security Policy) verwenden, um Ihre Remotedomäne zu aktivieren, und Browser überspringen möglicherweise die Überprüfung der CORS OPTIONS-Anforderungen.

Wenn ich etwas Zeit finde, werde ich das testen und diesen Beitrag aktualisieren!

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

CSP-Spezifikation: https://www.w3.org/TR/CSP/

Arnaud Tournier
quelle
Ich habe gerade getestet und es funktioniert nicht, CORS wird noch nach CSP für xhr Anfrage Zulassung benötigt ...
Arnaud Tournier