CORS spiegato: preflight, header e come risolvere gli errori CORS
CORS spiegato
CORS spiegato: preflight, header, errori e soluzioni | We.Inc
Come funziona CORS: richieste semplici e preflight, ogni header Access-Control, le regole sulle credenziali, gli errori più comuni in console e una soluzione lato server per Express, Next.js e nginx.
CORS (cross-origin resource sharing) è il modo in cui un server dice al browser che il JavaScript proveniente da un'altra origine può leggere le sue risposte. Il server risponde con Access-Control-Allow-Origin e gli header correlati. Le richieste che usano altri metodi, header personalizzati o body JSON inviano prima un preflight OPTIONS. La soluzione va sempre sul server che stai chiamando, mai sulla pagina.
Gli header di risposta CORS in sintesi
Header
Cosa consente
Access-Control-Allow-Origin
Quale origine può leggere la risposta (una sola origine oppure *)
Access-Control-Allow-Methods
Metodi consentiti dopo un preflight
Access-Control-Allow-Headers
Header di richiesta consentiti dopo un preflight
Access-Control-Allow-Credentials
Cookie e autenticazione inviati con la richiesta (il valore deve essere true)
Access-Control-Expose-Headers
Header di risposta che lo script può leggere
Access-Control-Max-Age
Secondi per cui la risposta a un preflight può restare in cache
Un errore CORS sembra un bug nel tuo codice, eppure di solito la richiesta è arrivata al server senza problemi. Il browser si è semplicemente rifiutato di passare la risposta al tuo script. Il simulatore qui sotto ti permette di descrivere una richiesta e la configurazione del server, poi ti dice se parte un preflight, se il browser lascia passare la risposta e quale messaggio vedresti in console.
Imposta l'origine della pagina, il metodo, un eventuale header personalizzato e se vengono inviati i cookie.
Imposta cosa risponde il server: origine consentita, metodi, header e il flag delle credenziali.
Leggi il verdetto: se serve un preflight, se viene superato e il motivo mostrato in console se fallisce.
Copia lo snippet server corrispondente qui sotto e aggiungilo all'API, non al front end.
Quando ci sono di mezzo i cookie, restituisci l'origine esatta che fa la richiesta prendendola da una allowlist, perché il carattere jolly viene rifiutato con le credenziali.
Aggiungi Vary: Origin ogni volta che l'origine consentita cambia da una richiesta all'altra, così le cache non servono la risposta di un sito a un altro.
Rispondi a OPTIONS con uno stato 2xx e senza redirect. Un preflight che finisce su un redirect al login fallisce sempre.
Disattivare CORS nel tuo browser nasconde il problema solo sul tuo computer. I visitatori continuano a vedere l'errore.
Le chiamate da server a server non coinvolgono mai CORS. Se non controlli l'API, chiamala invece dal tuo backend.
Domande frequenti
Perché Postman o curl funzionano mentre il browser fallisce?
CORS viene applicato solo dai browser, per impedire agli script di un sito di leggere i dati di un altro sito usando i tuoi cookie. Strumenti come curl non eseguono codice di pagine non affidabili, quindi lo ignorano. La risposta del server era identica: è stato solo il browser a decidere di trattenerla.
Cosa fa partire una richiesta di preflight?
Qualsiasi metodo diverso da GET, HEAD o POST, qualsiasi header al di fuori dei pochi ammessi (Accept, Accept-Language, Content-Language, Content-Type, Range), oppure un Content-Type diverso da codifica form, multipart o text/plain. Inviare application/json è la causa più comune.
Posso risolvere CORS dal frontend?
No. Impostare gli header Access-Control sulla tua richiesta non serve a nulla, e mode: no-cors ti restituisce una risposta opaca che il tuo script non può leggere. O l'API aggiunge gli header, oppure è il tuo server a recuperare i dati e a passarli avanti.
Posso usare Access-Control-Allow-Origin: * con i cookie?
No. Quando una richiesta porta credenziali, il browser rifiuta un'origine jolly e richiede l'origine esatta più Access-Control-Allow-Credentials: true. Lo stesso vale per i caratteri jolly negli elenchi di metodi, header ed expose headers.
Perché il mio script non riesce a leggere un header di risposta personalizzato?
Di default, uno script cross-origin vede solo un breve elenco di header di risposta sicuri. Elenca gli altri in Access-Control-Expose-Headers, per esempio X-Request-Id, e diventano leggibili.
Stessa origine, e perché il browser blocca le letture
Un'origine è l'insieme di schema, host e porta. https://app.example.com e https://api.example.com sono origini diverse, e lo sono anche le versioni http e https dello stesso dominio. Di default uno script può inviare una richiesta a un'altra origine, ma non può leggere ciò che torna indietro.
CORS è il consenso esplicito. L'API elenca le origini di cui si fida e il browser applica quell'elenco per conto del visitatore. Ecco perché l'header deve arrivare dal server che chiami.
Richieste semplici e preflight
Una semplice GET, o una POST da un normale form, parte direttamente con un header Origin. Il browser poi controlla che la risposta contenga un'origine consentita prima di passarla al tuo codice.
Tutto il resto invia prima una richiesta OPTIONS con Access-Control-Request-Method e Access-Control-Request-Headers. La richiesta vera parte solo se il server approva entrambi. Access-Control-Max-Age permette al browser di ricordare l'approvazione, anche se i browser limitano per quanto tempo.
Leggere l'errore in console
Per motivi di sicurezza, il tuo JavaScript sa solo che la fetch è fallita, con un TypeError e nessun dettaglio. Il motivo reale compare nella console del browser e indica l'header mancante o sbagliato. Confrontalo con la tabella dei messaggi qui sotto.
Errori CORS comuni e come risolverli
Il testo dei messaggi cambia da browser a browser. Questi sono i motivi come li chiama Firefox su MDN; Chrome dice le stesse cose con frasi più lunghe.
Motivi CORS in console e la soluzione lato server
Motivo in console
Cosa significa
Soluzione sul server
CORS header 'Access-Control-Allow-Origin' missing
La risposta non conteneva alcun header allow-origin
Aggiungi l'header a ogni risposta, comprese quelle di errore
CORS header 'Access-Control-Allow-Origin' does not match
L'header indica un'origine diversa
Restituisci l'origine del chiamante presa dalla tua allowlist
Credential is not supported if the CORS header 'Access-Control-Allow-Origin' is '*'
Sono stati inviati cookie ma il server ha risposto con *
Restituisci l'origine esatta e Allow-Credentials: true
expected 'true' in CORS header 'Access-Control-Allow-Credentials'
Credenziali inviate ma il flag è assente
Invia Access-Control-Allow-Credentials: true
Did not find method in CORS header 'Access-Control-Allow-Methods'
Il preflight non ha approvato il metodo
Elenca PUT, PATCH o DELETE in Allow-Methods
missing token in CORS header 'Access-Control-Allow-Headers' from CORS preflight channel
Un header di richiesta come Authorization non è stato approvato
Elencalo in Allow-Headers
CORS preflight channel did not succeed
OPTIONS è fallita, ha fatto un redirect o ha restituito un errore
Rispondi a OPTIONS con 204 e gli header
Multiple CORS header 'Access-Control-Allow-Origin' not allowed
Due livelli (app e proxy) hanno aggiunto entrambi l'header
Impostalo in un solo punto
Snippet per il server
Express: allowlist con credenziali
Restituisce l'origine solo quando è nell'elenco, risponde ai preflight con 204 e aggiunge Vary così le cache tengono separate le origini.
Route handler di Next.js
Un endpoint pubblico e senza cookie può usare il carattere jolly. Esporta OPTIONS così il preflight riceve una risposta tutta sua.
nginx davanti a un'API
Il flag always mantiene l'header anche sulle risposte di errore, così un 500 mostra comunque il suo stato reale invece di un errore CORS.
Lato browser: inviare cookie cross-origin
credentials: "include" è ciò che attiva sul server le regole più severe sulle credenziali.
Simulatore di richieste CORS
Descrivi una richiesta e gli header CORS del server per vedere se viene inviato un preflight e se il browser permette al tuo script di leggere la risposta.
La richiesta
La risposta del server
Origine della pagina
Origine dell'API
Metodo
Content-Type
Nessun body
Header di richiesta aggiuntivo (vuoto per nessuno)
Invia cookie (credentials: include)
Access-Control-Allow-Origin
Access-Control-Allow-Methods
Access-Control-Allow-Headers
Access-Control-Allow-Credentials: true
Verdetto
Stessa origine: CORS non si applica, la risposta è leggibile.
Nessun preflight: questa è una richiesta semplice.
Serve un preflight: il browser invia prima OPTIONS.
Motivo: il metodo non è GET, HEAD o POST.
Motivo: l'header {v} non è tra quelli ammessi.
Motivo: il Content-Type {v} non è uno dei tre tipi semplici.
Consentito: il tuo script può leggere la risposta.