CORS è un meccanismo del browser con cui un server dichiara quali altre origin possono chiamarlo, allentando di proposito la same-origin policy. Due riformulazioni dissolvono quasi tutta la confusione: è il browser a imporlo (non il server, ed è per questo che curl funziona), ed è il server a correggerlo (non il frontend, ed è per questo che nessuna quantità di JavaScript cambia le cose). Tutto il resto sono header.
Punti chiave
| Domanda | Risposta |
|---|---|
| Il fondamento | Same-Origin Policy: per impostazione predefinita gli script non possono leggere le risposte di altre origin |
| Un’origin | schema + host + porta – tutti e tre devono corrispondere |
| CORS | Header del server che abilitano origin precise; il browser impone |
| Preflight | Un OPTIONS “posso?” prima delle richieste non semplici |
| La verità eterna | Gli errori CORS si correggono sul server, punto |
Tutto il protocollo, sul filo
# Preflight: il browser chiede prima di un PUT con un header di autenticazione
OPTIONS /classes/Product HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: x-parse-session-token
# Il permesso rilasciato dal server
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com ← questa origin può leggere
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: x-parse-session-token
Access-Control-Max-Age: 7200 ← risposta in cache; nessun preflight ripetuto
# Allora, e solo allora, parte la richiesta vera.
Come si presenta dal codice applicativo – e la verità di piattaforma che insegnano le schede: CORS è una questione di browser, che le app native non incontrano mai:
// Browser JavaScript — Back4app JS SDK
// A cross-origin call that just works: the platform answers the
// preflight and sends the CORS headers, so the browser lets it through
Parse.initialize('APP_ID', 'JS_KEY');
Parse.serverURL = 'https://parseapi.back4app.com'; // different origin than your site
const products = await new Parse.Query('Product').find();
// No proxy hacks, no "disable CORS" — the server side is configured correctly. // Flutter / Dart — Back4app Flutter SDK
// Native apps have no same-origin policy — CORS is a browser concern.
// Flutter Web, however, DOES enforce it: same browser rules apply there.
final query = QueryBuilder<ParseObject>(ParseObject('Product'));
final response = await query.query();
// Works identically on mobile and web because the server sends CORS headers. // iOS / Swift — Back4app Swift SDK
// No browser, no same-origin policy: CORS never applies to native iOS.
// The same backend serves browsers (with CORS headers) and apps alike.
let query = Product.query()
query.find { result in
if case .success(let products) = result { render(products) }
} // Android / Kotlin — Back4app Android SDK
// No browser, no same-origin policy: CORS never applies to native Android.
// The same backend serves browsers (with CORS headers) and apps alike.
val query = ParseQuery.getQuery<ParseObject>("Product")
query.findInBackground { products, e ->
if (e == null) render(products)
} Il flusso, deciso
Due dettagli reggono la maggior parte delle sessioni di debug: una richiesta semplice (GET/HEAD/POST con header della safelist e content type basilari) salta il preflight – ed è per questo che aggiungere un header Authorization all’improvviso “rompe” un endpoint che funzionava; e le richieste con credentials stringono tutto – i cookie circolano solo con Access-Control-Allow-Credentials: true più un’origin esatta, mai il wildcard, secondo la specifica.
Come correggere gli errori CORS più comuni (errore → causa → correzione)
| La console dice | Che cosa è successo davvero | Correzione (sempre lato server) |
|---|---|---|
| No ‘Access-Control-Allow-Origin’ header | Il server non ha mai abilitato quell’origin | Aggiungere la propria origin all’elenco consentito |
| Origin not allowed by Access-Control-Allow-Origin | L’allowlist esiste; quell’origin non c’è | Aggiungere schema+host+porta esatti |
| Response to preflight… doesn’t pass | OPTIONS non gestito oppure header incompleti | Rispondere a OPTIONS con metodi/header |
| Wildcard ’*’ cannot be used with credentials | Cookie + * – combinazione vietata | Elencare le origin in modo esplicito |
| Request header not allowed | Header personalizzato assente dall’allowlist | Aggiungerlo ad Access-Control-Allow-Headers |
E l’anti-correzione che vale la pena nominare: disattivare CORS con un flag del browser o passare da proxy di sviluppo permissivi rende l’errore invisibile solo su una macchina – il deployment resta rotto per gli utenti. La correzione vera è una configurazione del server che si misura in minuti.
Casi d’uso tipici
- SPA + API su origin diverse – app.example.com che chiama api.example.com: il caso quotidiano per cui CORS esiste.
- Sviluppo locale – localhost:3000 contro un backend reale; conviene consentire l’origin di sviluppo, non disattivare lo scudo.
- API pubbliche – origin wildcard, senza credentials: corretto e sicuro per dati davvero pubblici.
- Piattaforme multi-frontend – più app, un solo backend, un elenco esplicito di origin per ambiente.
- Backend BaaS – la piattaforma risponde ai preflight e invia gli header; resta soltanto da dichiarare le origin consentite.
Wildcard vs. origin esplicite: matrice decisionale
| Configurazione | Giusta quando… | Mai quando… |
|---|---|---|
Wildcard * | API pubblica, senza credentials | Esistono cookie o sessioni utente |
| Elenco esplicito di origin | API private, app con credentials | – (è questa la risposta predefinita) |
| Riflettere l’origin della richiesta | Quasi mai | In combinazione con i credentials – la falla classica |
| Elenchi per ambiente | Igiene fra sviluppo, staging e produzione | La produzione eredita le voci localhost dello sviluppo |
Una riformulazione di sicurezza chiude la matrice: CORS non è controllo degli accessi – le app native e i server lo ignorano del tutto, quindi protegge gli utenti del browser dai siti malevoli, non l’API da chi la chiama. Quel lavoro lo fanno l’autenticazione e i permessi al livello dei dati; CORS decide soltanto quali script di quali siti possono leggere le risposte.
Limiti e trade-off
- Governa soltanto i browser. Tutto ciò che non è un browser gli passa davanti – un elenco di origin non va mai confuso con un’autorizzazione.
- I preflight costano un round trip sulle richieste non semplici; la messa in cache via
Access-Control-Max-Ageè la correzione a basso costo che tutti dimenticano. - Una configurazione sbagliata fallisce chiusa e in modo rumoroso – bene per la sicurezza, brutale per il debug senza la tabella errore→correzione qui sopra.
- Gli elenchi di origin sono stato dell’ambiente. Le origin di staging si infilano nelle configurazioni di produzione; l’elenco va verificato come qualsiasi credenziale.
- CORS ≠ CSRF ≠ CSP. Acronimi vicini, difese distinte – i cookie hanno comunque bisogno di protezione SameSite o a token, e le pagine di una content policy, mentre CORS gestisce soltanto la lettura cross-origin.
CORS su Back4app
Back4app è una piattaforma open-source di Backend as a Service (BaaS) che combina database gestito, API REST e GraphQL generate automaticamente, autenticazione, archiviazione di file e funzioni serverless con Cloud Code. La scocciatura CORS arriva già risolta: le API della piattaforma rispondono ai preflight e inviano gli header corretti, così le app nel browser chiamano il backend dalle origin consentite senza trucchi con i proxy – la scheda JavaScript qui sopra è tutta l’esperienza – mentre le schede Flutter, Swift e Kotlin dimostrano la verità più discreta: i client nativi non incontrano mai la cerimonia. La sicurezza vera resta dove deve stare: sessioni, ACL e permessi di classe applicati lato server a ogni richiesta, da qualunque origin.
Domande frequenti
Che cos'è CORS in parole semplici?
Il sistema di permessi del browser per le chiamate API fra siti diversi. Per impostazione predefinita, gli script di un'origin non possono leggere le risposte di un'altra: è la Same-Origin Policy. CORS è il modo in cui un server dà il proprio consenso: header di risposta che dichiarano quali origin, metodi e header accetta. Il browser impone; il server dichiara; il codice frontend è solo il messaggero.
Che cos'è esattamente un'origin?
La tripletta schema, host e porta. https://app.example.com e https://api.example.com sono origin diverse (differisce l'host); lo sono anche le versioni http e https di uno stesso sito (differisce lo schema) e :3000 vs :8080 in sviluppo (differisce la porta). La parola inglese indica appunto l'origine di una pagina: ogni decisione CORS confronta queste tre parti, e nient'altro dell'URL conta.
Che cosa provoca il classico errore CORS?
Il browser ha chiamato un'altra origin e la risposta non conteneva un header Access-Control-Allow-Origin corrispondente: il browser ha quindi impedito allo script di leggerla. Spesso la richiesta ha raggiunto il server senza problemi; il blocco avviene lato client, sulla lettura. È per questo che la correzione sta sempre nella configurazione del server, mai nel codice frontend.
Che cos'è una richiesta preflight?
Il controllo di permesso che il browser esegue in anticipo per le richieste non semplici: prima di inviare un PUT, un DELETE o qualsiasi cosa con header personalizzati come un token di autorizzazione, invia una richiesta OPTIONS che chiede "posso?". Gli header del server rispondono quali metodi, header e origin sono consentiti; solo allora parte la richiesta vera. I preflight si possono mettere in cache con Access-Control-Max-Age.
Perché una richiesta funziona con curl e fallisce nel browser?
Perché CORS è un vincolo imposto dal browser, non un rifiuto del server. Strumenti come curl e le app mobile native non hanno una same-origin policy, quindi leggono la risposta senza batter ciglio. Il browser applica la politica per conto della persona che lo usa: la differenza che si osserva è il punto di applicazione, ed è anche la prova che CORS non è controllo degli accessi.
Access-Control-Allow-Origin: * è sicuro?
Per API davvero pubbliche e senza credentials, sì. Il wildcard è vietato in modalità con credentials – i browser rifiutano per progetto di associarvi i cookie – e riflettere origin arbitrarie consentendo al tempo stesso i credentials è la configurazione errata classica che trasforma CORS da scudo in falla. Le API private elencano le origin in modo esplicito.
Si può semplicemente disattivare CORS per risolvere l'errore?
Solo nel senso in cui togliere il rilevatore di fumo spegne un incendio. I flag del browser e i proxy permissivi mascherano il sintomo su una sola macchina mentre consegnano il guasto a ogni utente. La correzione giusta richiede pochi minuti: configurare il server (o la dashboard della piattaforma) perché consenta le origin che devono chiamarlo.
Qual è la differenza tra CORS, CSRF e CSP?
Tre compiti diversi. CORS regola quali origin possono leggere le risposte di un server. CSRF è un attacco – sfruttare i cookie di una persona per forgiare richieste – contrastato da token e cookie SameSite, non da CORS. CSP è la politica propria di una pagina, che limita ciò che può caricare ed eseguire: lo strumento anti-XSS. Acronimi vicini, meccanismi ortogonali.