CORS è un header di risposta. Dice quale origin può leggere. Non chiude l'endpoint. Il jolly non sta con i cookie. Il preflight è un OPTIONS. Un plugin «enable CORS» non è una policy.
Un plugin «abilita CORS» non è una policy. Cross-Origin Resource Sharing è un insieme di header di risposta con cui il server dice al browser quale origin può leggere quella risposta. Non dice chi può chiamare l’URL. La chiamata parte lo stesso. Ciò che il browser nega, se gli header mancano, è consegnare il body a JavaScript.
In breve: Access-Control-Allow-Origin con un origin preciso, o * solo senza credenziali. Cookie e Authorization vogliono Access-Control-Allow-Credentials: true e l’origin eco, non il jolly. Il preflight è un OPTIONS. null non si usa. Verifica del 20 settembre 2026 su MDN e sulla Fetch Standard.
Cosa trovi in questo articolo
Cosa è CORS (e cosa non è un firewall)
MDN, nella guida Cross-Origin Resource Sharing, parte da un vincolo del browser: una pagina su https://app.example che fa fetch() verso https://api.altro non legge la risposta, a meno che il server di api.altro non lo consenta con gli header CORS. Lo stesso vale per XMLHttpRequest, per i font in @font-face da un altro origin, per le texture WebGL, per le immagini disegnate su canvas con drawImage().
La Fetch Standard, che oggi definisce CORS, distingue due piani. Il browser spedisce la richiesta. Il server la riceve, la esegue, risponde. Se gli header CORS non tornano, JavaScript vede solo un errore. Per ragioni di sicurezza, scrive MDN, i dettagli di quell’errore non sono disponibili allo script: si leggono in console. CORS non è un WAF. Non è HSTS. Non è la CSP. HSTS impedisce di tornare in HTTP sul nome. La CSP limita cosa la pagina può eseguire. CORS limita quale origin può leggere una risposta cross-origin. Tre mestieri. Il plugin che promette tutti e tre è un quarto script.
Questo pezzo sta nel cluster Sicurezza, accanto a Subresource Integrity — che senza CORS sul CDN fallisce — al cookie con SameSite e al mixed content. SRI verifica i byte. SameSite decide se il cookie parte. Mixed content è HTTP su una pagina HTTPS. CORS è chi, in pagina, può leggere la risposta.
Allow-Origin: *, un origin, mai null
L’header Access-Control-Allow-Origin è Baseline, disponibile nei browser da luglio 2015. MDN elenca tre forme. Una sola è quella che si usa in produzione con testa.
| Valore | Cosa fa il browser |
|---|---|
* | qualsiasi origin può leggere la risposta. Solo su richieste senza credenziali. Con cookie o Authorization, il jolly produce un errore |
un origin preciso (https://app.example) | solo quel origin. Se i client sono più di uno, il server deve ecoare l’origin della richiesta, non una lista |
null | non si usa. MDN: documenti data:, file: e iframe sandbox serializzano origin null; un attaccante può costruirne uno ostile |
Il jolly sul CDN pubblico di un CSS, senza cookie, è un caso. Lo stesso jolly sull’API che restituisce la sessione è un buco. MDN, sull’header Access-Control-Allow-Credentials: se vale true, Access-Control-Allow-Origin non può essere *. Si rimanda l’origin della richiesta. Si aggiunge Vary: Origin, altrimenti la cache consegna a un secondo origin la risposta pensata per il primo.
Una richiesta «semplice», nel vocabolario dell’obsoleta spec CORS — la Fetch Standard non usa più quel nome — è un GET, HEAD o POST che somiglia a un form HTML 4. Il server deve già difendersi dal CSRF su quei verb. CORS, su quelle, non chiede un OPTIONS: chiede comunque Access-Control-Allow-Origin se lo script deve leggere la risposta. Un form può spedire. JavaScript, senza header, non legge.
Preflight, credenziali, Vary
Se il metodo non è GET/HEAD/POST, o se ci sono header non safelisted — Content-Type: application/json è il caso di scuola — il browser fa prima un OPTIONS. Sull’OPTIONS mette Access-Control-Request-Method e, se serve, Access-Control-Request-Headers. Il server risponde con Access-Control-Allow-Methods, Access-Control-Allow-Headers, eventualmente Access-Control-Max-Age. Poi, se l’OPTIONS torna approvato, parte la richiesta vera. MDN: i fallimenti CORS non arrivano allo script con il codice HTTP; arrivano come network error. Si guarda la console, non il catch che stampa «failed to fetch».
| Header | Ruolo |
|---|---|
Access-Control-Allow-Origin | chi può leggere. Un origin, o * senza credenziali |
Access-Control-Allow-Credentials | true se devono partire cookie o HTTP auth. Esclude il jolly |
Access-Control-Allow-Methods | metodi ammessi dopo il preflight |
Access-Control-Allow-Headers | header ammessi sulla richiesta vera |
Access-Control-Max-Age | quanto il browser può cacheare il preflight |
Vary: Origin | obbligatorio se l’Allow-Origin è ecoato: la cache non deve mescolare gli origin |
Il plugin che scrive Access-Control-Allow-Origin: * su ogni risposta «per far partire la SPA» risolve la console e apre la lettura a qualunque sito. Se in più mette Allow-Credentials: true, i browser rifiutano la coppia: jolly e credenziali non stanno insieme. Il risultato, in pagina, è lo stesso errore di prima, con una policy peggiore in testa. Non è un’API «aperta in modo sicuro». È un header copiato da un tutorial.
SRI, sul CDN, chiede CORS più crossorigin="anonymous": senza, l’hash non parte. Sta nell’articolo su Subresource Integrity. Lì l’header serve a non filtrare il contenuto dal successo o dal fallimento del load. Qui serve a decidere chi, in JavaScript, vede il JSON. Stesso meccanismo, due intenzioni. Il certificato TLS cifra il transito; non sostituisce CORS.
Errori da evitare
Mettere
Access-Control-Allow-Origin: *insieme aAllow-Credentials: true: i browser lo rifiutano.Restituire
null«perché in locale funzionava»: MDN lo sconsiglia, origin ostili inclusi.Trattare CORS come autenticazione: la richiesta arriva al server anche se lo script non legge la risposta.
Dimenticare
Vary: Originquando si ecoa l’origin: la cache mescola le risposte.Confondere CORS con la CSP o con HSTS: tre header, tre mestieri.
Fidarsi del plugin «enable CORS» senza leggere gli header in risposta, in staging, con e senza cookie.
In sintesi
CORS è un header di risposta. Dice quale origin può leggere. Non chiude l’endpoint. Il jolly non sta con i cookie. Il preflight è un OPTIONS. null non si usa. Si verifica in console e sugli header, non sul lucchetto della home. MDN e la Fetch Standard, 20 settembre 2026.