CORS: Capirlo e Risolverlo Una Volta per Tutte
CORS: capirlo e risolverlo una volta per tutte. Guida completa agli errori Cross-Origin, preflight request e come configurare il server correttamente.
Cos'è CORS
CORS (Cross-Origin Resource Sharing) è un meccanismo di sicurezza implementato dai browser che controlla come le pagine web possono richiedere risorse da domini diversi da quello che ha servito la pagina. È uno degli errori più comuni e frustranti per gli sviluppatori web, ma capirlo a fondo lo rende semplice da risolvere.
Same-Origin Policy
Definizione di Origin
Due URL hanno la stessa origin solo se hanno identici protocollo, host e porta:
// Stessa origin
https://example.com/page1
https://example.com/page2/subpage
// Origine diversa (cross-origin)
https://example.com vs http://example.com (protocollo)
https://example.com vs https://api.example.com (host)
https://example.com vs https://example.com:8080 (porta)
https://example.com vs https://other.com (dominio)
Perché Esiste
La Same-Origin Policy protegge gli utenti da attacchi malevoli:
- Session hijacking: Un sito malevolo non può leggere cookie di altri siti
- Data theft: JavaScript di un sito non può accedere a dati di altri
- CSRF: Limita le richieste cross-origin non autorizzate
L'Errore CORS
Messaggio Tipico
Access to fetch at 'https://api.example.com/data'
from origin 'https://myapp.com' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Cosa Significa
Il browser ha bloccato la risposta perché il server non ha incluso gli header CORS necessari. La richiesta è stata effettivamente inviata e il server ha risposto, ma il browser non permette al JavaScript di accedere alla risposta.
Come Funziona CORS
Richieste Simple
Alcune richieste sono considerate "simple" e non richiedono preflight:
// Condizioni per richiesta simple:
// 1. Metodo: GET, HEAD, POST
// 2. Headers: solo Accept, Accept-Language, Content-Language, Content-Type
// 3. Content-Type: text/plain, multipart/form-data, application/x-www-form-urlencoded
// Esempio simple request
fetch('https://api.example.com/data')
.then(response => response.json());
// Il browser aggiunge automaticamente:
// Origin: https://myapp.com
// Il server deve rispondere con:
// Access-Control-Allow-Origin: https://myapp.com
Preflight Request
Richieste "non simple" richiedono una richiesta OPTIONS preliminare:
// Questa richiesta triggera un preflight
fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json', // Non simple!
'Authorization': 'Bearer token' // Custom header!
},
body: JSON.stringify({ data: 'value' })
});
// 1. Browser invia OPTIONS (preflight)
OPTIONS /data HTTP/1.1
Host: api.example.com
Origin: https://myapp.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
// 2. Server risponde con permessi
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
// 3. Browser invia richiesta reale
POST /data HTTP/1.1
...
// 4. Server risponde con dati + CORS headers
Headers CORS
Response Headers (Server)
# Headers che il server deve inviare
Access-Control-Allow-Origin: https://myapp.com
# Oppure per tutti (non usare con credentials!):
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
# Metodi permessi
Access-Control-Allow-Headers: Content-Type, Authorization, X-Custom-Header
# Headers custom permessi
Access-Control-Allow-Credentials: true
# Permetti cookie/auth (richiede origin specifica, non *)
Access-Control-Expose-Headers: X-Custom-Response-Header
# Headers che JS può leggere dalla risposta
Access-Control-Max-Age: 86400
# Cache preflight per 24 ore
Request Headers (Browser)
# Headers che il browser aggiunge automaticamente
Origin: https://myapp.com
# Origin della richiesta (sempre presente)
Access-Control-Request-Method: POST
# Metodo che verrà usato (solo in preflight)
Access-Control-Request-Headers: Content-Type, Authorization
# Headers custom che verranno usati (solo in preflight)
Configurazione Server
Node.js/Express
// Con middleware cors
const cors = require('cors');
// Configurazione base
app.use(cors());
// Configurazione specifica
app.use(cors({
origin: 'https://myapp.com',
// Oppure funzione per logica dinamica:
// origin: (origin, callback) => { ... }
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 86400
}));
// Configurazione manuale senza middleware
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'https://myapp.com');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.header('Access-Control-Allow-Credentials', 'true');
if (req.method === 'OPTIONS') {
return res.sendStatus(204);
}
next();
});
Next.js API Routes
// pages/api/data.js
export default function handler(req, res) {
// Set CORS headers
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
if (req.method === 'OPTIONS') {
return res.status(200).end();
}
// Handle request
res.json({ data: 'value' });
}
// next.config.js per headers globali
module.exports = {
async headers() {
return [{
source: '/api/:path*',
headers: [
{ key: 'Access-Control-Allow-Origin', value: '*' }
]
}];
}
};
Nginx
# nginx.conf
location /api/ {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '$http_origin';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
add_header 'Access-Control-Max-Age' 86400;
return 204;
}
add_header 'Access-Control-Allow-Origin' '$http_origin' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
proxy_pass http://backend;
}
Credentials e Cookie
// Client: include credentials
fetch('https://api.example.com/data', {
credentials: 'include' // Invia cookie
});
// Server: deve rispondere con
Access-Control-Allow-Origin: https://myapp.com // NON può essere *
Access-Control-Allow-Credentials: true
// IMPORTANTE: con credentials:
// - Origin non può essere *
// - Devi specificare l'origin esatta
// - Il server deve verificare l'origin
Soluzioni Comuni
Proxy Server
// Invece di chiamare API direttamente, usa proxy
// vite.config.js
export default {
server: {
proxy: {
'/api': {
target: 'https://api.example.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
};
// Ora: fetch('/api/data') → proxied to api.example.com/data
// Nessun problema CORS (same origin)
Backend for Frontend (BFF)
// Crea un endpoint nel tuo backend che chiama l'API esterna
// /api/external-data
app.get('/api/external-data', async (req, res) => {
const response = await fetch('https://external-api.com/data', {
headers: { 'API-Key': process.env.API_KEY }
});
const data = await response.json();
res.json(data);
});
// Il frontend chiama il tuo backend (same origin)
// Il backend chiama l'API esterna (server-to-server, no CORS)
Debugging CORS
// 1. Apri DevTools → Network
// 2. Cerca la richiesta fallita
// 3. Verifica:
// - Origin header nella request
// - Access-Control-* headers nella response
// - Se c'è stata una preflight OPTIONS
// 4. Controlla la console per errori specifici
// Errori comuni:
"No 'Access-Control-Allow-Origin' header"
→ Server non invia header CORS
"The value of the 'Access-Control-Allow-Origin' header...
must not be the wildcard '*' when credentials mode is 'include'"
→ Usa origin specifica invece di *
"Method PUT is not allowed"
→ Aggiungi PUT a Access-Control-Allow-Methods
Strumenti Correlati
Per debug API e web:
- JSON Formatter - Formatta risposte API
- Base64 Decoder - Decodifica token JWT
- URL Encoder - Codifica parametri
Conclusione
CORS non è il nemico—è una protezione importante. Per risolverlo:
- Configura il server: Aggiungi gli header CORS corretti
- Usa proxy in sviluppo: Evita CORS durante lo sviluppo
- BFF pattern: Per API esterne senza controllo
- Mai disabilitare: Non usare estensioni che disabilitano CORS
Per altri strumenti utili, esplora i nostri tool online gratuiti. Per approfondimenti, consulta MDN CORS Documentation.