Perché viene inviata una richiesta OPTIONS e posso disabilitarla?


415

Sto creando un'API Web. Ho trovato ogni volta che utilizzo Chrome per POST, ARRIVO alla mia API, c'è sempre una richiesta OPTIONS inviata prima della richiesta reale, il che è abbastanza fastidioso. Attualmente ottengo che il server ignori qualsiasi richiesta OPTIONS. Ora le mie domande sono utili per inviare una richiesta OPTIONS per raddoppiare il carico del server? Esiste un modo per impedire completamente al browser di inviare richieste OPTIONS?

Risposte:


376

modifica 13/09/2018 : aggiunte alcune precisazioni su questa richiesta pre-volo e su come evitarlo alla fine di questa risposta.

OPTIONSle richieste sono ciò in cui chiamiamo pre-flightrichieste Cross-origin resource sharing (CORS).

Sono necessari quando si effettuano richieste tra origini diverse in situazioni specifiche.

Questa richiesta pre-volo viene fatta da alcuni browser come misura di sicurezza per garantire che la richiesta in corso sia considerata attendibile dal server. Ciò significa che il server comprende che il metodo, l'origine e le intestazioni inviati sulla richiesta sono sicuri su cui agire.

Il tuo server non dovrebbe ignorare ma gestire queste richieste ogni volta che stai tentando di fare richieste incrociate.

Una buona risorsa può essere trovata qui http://enable-cors.org/

Un modo per gestirli per sentirsi a proprio agio è assicurarsi che per qualsiasi percorso con OPTIONSmetodo il server invii una risposta con questa intestazione

Access-Control-Allow-Origin: *

Questo dirà al browser che il server è disposto a rispondere alle richieste da qualsiasi origine.

Per ulteriori informazioni su come aggiungere il supporto CORS al server, consultare il diagramma di flusso seguente

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

Diagramma di flusso CORS


modifica 13/09/2018

La OPTIONSrichiesta CORS viene attivata solo in alcuni casi, come spiegato nei documenti MDN :

Alcune richieste non attivano un preflight CORS. Quelle sono chiamate "richieste semplici" in questo articolo, sebbene la specifica Fetch (che definisce CORS) non usi quel termine. Una richiesta che non attiva un preflight CORS, una cosiddetta "richiesta semplice", è una che soddisfa tutte le seguenti condizioni:

Gli unici metodi consentiti sono:

  • OTTENERE
  • TESTA
  • INVIARE

A parte le intestazioni impostate automaticamente dall'agente utente (ad esempio Connessione, Utente-Agente o una qualsiasi delle altre intestazioni con nomi definiti nelle specifiche di recupero come "nome di intestazione proibito"), le uniche intestazioni che possono essere impostati manualmente sono quelli che la specifica Fetch definisce come "intestazione di richiesta con elenco sicuro CORS", che sono:

  • Accettare
  • Accept-Language
  • Content-Language
  • Tipo di contenuto (ma notare i requisiti aggiuntivi di seguito)
  • DPR
  • downlink
  • Salvare i dati
  • Finestra-Width
  • Larghezza

Gli unici valori consentiti per l'intestazione Content-Type sono:

  • application / x-www-form-urlencoded
  • multipart / form-data
  • text / plain

Nessun listener di eventi è registrato su qualsiasi oggetto XMLHttpRequestUpload utilizzato nella richiesta; questi sono accessibili usando la proprietà XMLHttpRequest.upload.

Nessun oggetto ReadableStream viene utilizzato nella richiesta.


8
Ma non è realistico impostare questo flag di Chrome per tutti gli utenti generici.
Qian Chen,

37
Non è corretto affermare che sono richieste richieste di verifica preliminare quando si effettuano richieste di origine incrociata. Le richieste di verifica preliminare sono richieste solo in situazioni specifiche, ad esempio se si impostano intestazioni personalizzate o si effettuano richieste diverse da get, head e post.
Robin Clowers,

4
Stranamente, quando si effettua una richiesta CORS utilizzando jQuery, la libreria JavaScript evita specificamente di impostare l'intestazione personalizzata, insieme a un avvertimento per gli sviluppatori: per le richieste tra domini, visto che le condizioni per un preflight sono simili a un puzzle, noi semplicemente non impostarlo mai per esserne sicuro.
Sotto il radar,

3
Come mai se faccio un curla all'pi funziona, ma quando corro da Chrome ottengo l'errore?
SuperUberDuper,

5
@SuperUberDuper perché le richieste CORS e verifica preliminare sono una questione relativa al browser. È possibile simulare CORS aggiungendo Originun'intestazione alla richiesta per simulare come se la richiesta provenisse da un host specifico (ad es. Yourwebsite.com). Puoi anche simulare le richieste di verifica preliminare impostando il metodo HTTP di una richiesta OPTIONSe Access-Control-*Headers
Leo Correa,

234

Ho superato questo problema, di seguito sono riportate le mie conclusioni su questo problema e la mia soluzione.

Secondo la strategia CORS (consiglio vivamente di leggerlo) Non puoi semplicemente forzare il browser a interrompere l'invio della richiesta OPTIONS se ritiene che sia necessario.

Esistono due modi per aggirare il problema:

  1. Assicurati che la tua richiesta sia una "richiesta semplice"
  2. Impostare Access-Control-Max-Ageper la richiesta OPTIONS

Semplice richiesta

Una semplice richiesta tra siti è quella che soddisfa tutte le seguenti condizioni:

Gli unici metodi consentiti sono:

  • OTTENERE
  • TESTA
  • INVIARE

A parte le intestazioni impostate automaticamente dal programma utente (ad es. Connessione, Utente-Agente, ecc.), Le uniche intestazioni che possono essere impostate manualmente sono:

  • Accettare
  • Accept-Language
  • Content-Language
  • Tipo di contenuto

Gli unici valori consentiti per l'intestazione Content-Type sono:

  • application / x-www-form-urlencoded
  • multipart / form-data
  • text / plain

Una semplice richiesta non provocherà una richiesta OPZIONI pre-volo.

Impostare una cache per il controllo OPTIONS

È possibile impostare un Access-Control-Max-Ageper la richiesta OPTIONS, in modo che non controllerà nuovamente l'autorizzazione fino alla scadenza.

Access-Control-Max-Age fornisce il valore in secondi per quanto tempo è possibile memorizzare nella cache la risposta alla richiesta di verifica preliminare senza inviare un'altra richiesta di verifica preliminare.

Limitazione notata

  • Per Chrome, il massimo dei secondi Access-Control-Max-Ageè 600che è di 10 minuti, secondo il codice sorgente di Chrome
  • Access-Control-Max-AgeFunziona solo per una risorsa ogni volta, ad esempio GETrichieste con lo stesso percorso URL ma query diverse verranno trattate come risorse diverse. Pertanto, la richiesta alla seconda risorsa attiverà comunque una richiesta di verifica preliminare.

3
Sì ... questa dovrebbe essere la risposta accettata e più rilevante per il quesiton ..!
Rajesh Mbm,

7
Grazie per averlo menzionato Access-Control-Max-Age. Questa è la chiave qui. Ti aiuta a evitare richieste di verifica preliminare eccessive.
Idris Mokhtarzada,

Sto usando axios per chiamare ottenere richiesta. Dove posso impostare Access-Control-Max-Age nella richiesta axios?
Mohit Shah,

+1 L'intestazione Access-Control-Max-Age è la chiave qui. Questa dovrebbe essere la risposta accettata! Ho impostato 86400 secondi (24 ore) sull'intestazione e la richiesta di prefligth è sparita!
revobtz,

1
@VitalyZdanevich no! Non evitarlo application/jsonsolo perché rende la tua richiesta non "semplice" (e quindi attiva CORS). Il browser sta facendo il suo lavoro. Imposta il tuo server per restituire qualcosa di simile a un'intestazione Access-Control-Max-Age: 86400e il browser non rinvia una richiesta OPTIONS per 24 ore.
colm.anseo,

139

Si prega di fare riferimento a questa risposta sull'effettiva necessità di una richiesta OPTIONS pre-decollata: CORS - Qual è la motivazione dietro l'introduzione delle richieste di verifica preliminare?

Per disabilitare la richiesta OPTIONS, le seguenti condizioni devono essere soddisfatte per la richiesta Ajax:

  1. La richiesta non imposta intestazioni HTTP personalizzate come 'application / xml' o 'application / json' ecc
  2. Il metodo di richiesta deve essere GET, HEAD o POST. Se il POST, tipo di contenuto dovrebbe essere uno dei application/x-www-form-urlencoded, multipart/form-dataotext/plain

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


14
+1 per "intestazioni HTTP personalizzate"! Nel mio caso, stavano causando l'attivazione della richiesta pre-volo. Ho riformulato la richiesta di inviare qualsiasi cosa stavo inviando nelle intestazioni poiché il corpo della richiesta e le richieste OPTIONS hanno smesso di essere inviate.
Andre

21
application/xmlo application/jsonnon sono "intestazioni HTTP personalizzate". L'intestazione stessa sarebbe Content-Typee chiamarla "personalizzata" sarebbe fuorviante.
Leo Correa,

1
Rimosse le intestazioni HTTP personalizzate e questo ha funzionato come un fascino!
Tim D,

47

Quando la console di debug è aperta e l' Disable Cacheopzione attivata, le richieste di verifica preliminare verranno sempre inviate (ovvero prima di ogni richiesta). se non disabiliti la cache, una richiesta pre-volo verrà inviata una sola volta (per server)


3
oh cosa sto pensando. avendo il debug per ore questa era la mia soluzione. cache disabilitata a causa della console di debug.
mauris,

1
Anche se la console di debug è chiusa, vengono inviate richieste di verifica preliminare
Luca Perico,

2
Luca: è vero, ma il punto è che la "disabilita cache" non ha alcun effetto quando gli strumenti di sviluppo sono chiusi. le richieste di verifica preliminare vengono inviate una sola volta (per server, ovviamente) se la cache non è disabilitata e vengono inviate prima di ogni richiesta se la cache è disabilitata.
Nir

È stato davvero utile.
Anuraag Patil,

38

Sì, è possibile evitare la richiesta di opzioni. La richiesta di opzioni è una richiesta di verifica preliminare quando si inviano (invii) dati a un altro dominio. È un problema di sicurezza del browser. Ma possiamo usare un'altra tecnologia: il livello di trasporto iframe. Consiglio vivamente di dimenticare qualsiasi configurazione CORS e utilizzare la soluzione readymade e funzionerà ovunque.

Dai un'occhiata qui: https://github.com/jpillora/xdomain

Ed esempio funzionante: http://jpillora.com/xdomain/


è in realtà una specie di proxy drop-in?
matanster

15
"La richiesta di opzioni è una richiesta di verifica preliminare quando si inviano (invii) dati a un altro dominio." - Non è vero. È possibile utilizzare XHR per inviare qualsiasi richiesta POST che è possibile inviare con un normale modulo HTML senza attivare una richiesta di verifica preliminare. È solo quando inizi a fare cose che un modulo non può fare (come tipi di contenuto personalizzati o intestazioni di richieste extra) che viene inviato un preflight.
Quentin,

La soluzione qui sembra basarsi su uno shim iframe che funziona in alcuni casi, ma presenta alcune importanti limitazioni. Cosa succede se si desidera conoscere il codice di stato HTTP della risposta o il valore di un'altra intestazione di risposta HTTP?
Stephen Crosby,

5
iFrames non sono stati creati per questo.
Romko,

1
questa è un'implementazione del software e risponde alla domanda finale che era: "Esiste un modo per impedire completamente al browser di inviare richieste OPTIONS?". Per farla breve, non c'è modo di disabilitarlo in Mozilla o Chromium, è implementato nel codice e le uniche opzioni "funzionanti" aggirano il comportamento.
scavenger

15

Per uno sviluppatore che comprende il motivo della sua esistenza ma deve accedere a un'API che non gestisce le chiamate OPTIONS senza autorizzazione, ho bisogno di una risposta temporanea in modo da poter sviluppare localmente fino a quando il proprietario dell'API aggiunge il corretto supporto SPA CORS o ottengo un'API proxy installato e funzionante.

Ho scoperto che puoi disabilitare CORS in Safari e Chrome su un Mac.

Disabilita la stessa politica di origine in Chrome

Chrome: Esci da Chrome, apri un terminale e incolla questo comando: open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir

Safari: disabilitazione dei criteri della stessa origine in Safari

Se vuoi disabilitare la stessa politica di origine su Safari (ho 9.1.1), devi solo abilitare il menu sviluppatore e selezionare "Disabilita restrizioni tra le origini" dal menu Sviluppo.


4
Dovresti mettere più in evidenza la parte che dice "questa non dovrebbe MAI essere una soluzione permament !!!!" . La stessa politica di origine è una misura di sicurezza del browser molto importante e non dovrebbe mai essere disabilitata durante la normale navigazione in Internet.
jannis,

Vorrei che il Web funzionasse in questo modo, quindi possiamo semplicemente richiedere i dati di cui abbiamo bisogno dai server senza problemi extra.
jemiloii,

14

Come già menzionato nei post precedenti, le OPTIONSrichieste sono presenti per un motivo. Se hai un problema con tempi di risposta elevati dal tuo server (es. Connessione oltremare) puoi anche fare in modo che il tuo browser memorizzi nella cache le richieste di verifica preliminare.

Chiedi al tuo server di rispondere con l' Access-Control-Max-Ageintestazione e per le richieste che vanno allo stesso endpoint la richiesta di verifica preliminare sarà stata memorizzata nella cache e non si verificherà più.


1
Grazie per questo! Il fatto che le OPTIONSrichieste vengano memorizzate nella cache con questa intestazione è piuttosto opaco in tutta la documentazione CORS che ho letto.
joshperry,

E la cache ha effetto solo con lo stesso url esatto. Voglio una cache di verifica preliminare a livello di dominio che può davvero ridurre i round trip. (CORS è sciocco!)
meraviglia

8

Ho risolto questo problema come.

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

È solo per lo sviluppo. Con questo aspetto 9ms e 500ms e non 8s e 500ms. Posso farlo perché l'app JS di produzione sarà sulla stessa macchina della produzione, quindi non ci sarà altro, OPTIONSma lo sviluppo è il mio locale.


4

Non puoi ma potresti evitare CORS usando JSONP.


2
Riceverai una richiesta OPTIONS solo se stai facendo qualcosa di non semplice . Puoi solo fare richieste semplici (GET, nessuna intestazione personalizzata, nessun dato di autenticazione) con JSONP, quindi JSONP non può sostituire qui.
Quentin,

Sì, lo so ma non conosco esattamente i requisiti del progetto. So che non è semplice evitare cors ma dipende dal progetto. Nel peggiore dei casi per evitare CORS è necessario passare i dati utilizzando i parametri get. Quindi, JSONP potrebbe sostituire cors a seconda delle esigenze del progetto (come hai detto, usando semplici richieste)
Jose Mato,

Non capisco il design del cosiddetto pre-volo. Cosa potrebbe non essere sicuro se il client decidesse di inviare dati al server? Non vedo che abbia senso raddoppiare il carico sul filo.
Qian Chen,

@ElgsQianChen questo può probabilmente rispondere alla tua domanda stackoverflow.com/questions/15381105/…
Leo Correa,

0

Dopo aver trascorso un'intera giornata e mezza a cercare di risolvere un problema simile, ho scoperto che aveva a che fare con IIS .

Il mio progetto API Web è stato impostato come segue:

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

Non avevo opzioni di configurazione specifiche CORS nel nodo web.config> system.webServer come ho visto in così tanti post

Nessun codice specifico CORS in global.asax o nel controller come decoratore

Il problema erano le impostazioni del pool di app .

La modalità pipeline gestita è stata impostata su classica ( modificata in integrata ) e Identity è stata impostata su Servizio di rete ( modificata in ApplicationPoolIdentity )

La modifica di tali impostazioni (e l'aggiornamento del pool di app) l'ha risolto per me.


-2

Quello che ha funzionato per me è stato importare "github.com/gorilla/handlers" e quindi usarlo in questo modo:

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

Non appena ho eseguito una richiesta POST Ajax e allego dati JSON, Chrome aggiungeva sempre l'intestazione Content-Type che non era nella mia precedente configurazione consentita.


-2

Una soluzione che ho usato in passato: supponiamo che il tuo sito sia su mydomain.com e che tu debba presentare una richiesta Ajax a foreigndomain.com

Configura una riscrittura IIS dal tuo dominio al dominio straniero - ad es

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

sul tuo sito mydomain.com: puoi quindi effettuare una stessa richiesta di origine e non è necessaria alcuna richiesta di opzioni :)


-2

Può essere risolto in caso di utilizzo di un proxy che intercetta la richiesta e scrive le intestazioni appropriate. Nel caso particolare di Varnish queste sarebbero le regole:

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

}


-5

Esiste forse una soluzione (ma non l'ho testato): potresti usare CSP (Content Security Policy) per abilitare il tuo dominio remoto e i browser potrebbero saltare la verifica della richiesta OPZIONI CORS.

Se trovo un po 'di tempo, lo testerò e aggiornerò questo post!

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

Specifiche CSP: https://www.w3.org/TR/CSP/


Ho appena testato e non funziona, CORS è ancora richiesto dopo CSP per l'ammissione della richiesta xhr ...
Arnaud Tournier
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.