Als je een rode CORS-fout in je browserconsole hebt gezien, betekent dit dat je frontend een verzoek naar een API stuurt, maar dat de browser weigert het antwoord aan je terug te geven. De klassieke melding luidt: Access to fetch at '...' from origin '...' has been blocked by CORS policy. Op het eerste gezicht lijkt het alsof de server is gecrasht, maar het verzoek bereikt de server meestal wel en er komt ook een antwoord terug — de browser geeft dat antwoord simpelweg niet door aan je JavaScript vanwege een beveiligingsregel. In dit artikel leg ik uit wat de fout echt is, de begrippen origin en preflight, en hoe je het probleem definitief oplost met de juiste response-headers.
Wat is een CORS-fout precies?
CORS staat voor Cross-Origin Resource Sharing. Standaard handhaven browsers de same-origin policy: JavaScript op een pagina mag alleen antwoorden lezen die tot dezelfde origin behoren. Een origin bestaat uit drie delen: protocol, domein en poort. Dus https://site.com, http://site.com, https://api.site.com en https://site.com:8080 zijn allemaal verschillende origins.
Als je frontend draait op http://localhost:3000 en je API op http://localhost:8000, dan zijn dat twee verschillende origins, en blokkeert de browser het antwoord tenzij de server expliciet toestemming geeft. CORS is de gestandaardiseerde manier om die toestemming te geven: de server voegt speciale headers aan zijn antwoord toe die zeggen "ik sta deze origin toe om data van mij te lezen".
Het cruciale onderscheid: de oplossing zit op de server, niet op de client
Dit is het meest voorkomende misverstand. CORS is een browser-mechanisme, maar de toestemming komt van de server. Je kunt de fout niet oplossen door fetch-opties aan de JavaScript-kant aan te passen; de oplossing is dat de API de juiste response-headers stuurt. De belangrijkste header is:
Access-Control-Allow-Origin: https://site.com
Als deze header ontbreekt in het antwoord, of niet overeenkomt met de origin die het verzoek doet, blokkeert de browser het. Wanneer je hetzelfde verzoek vanuit Postman of curl stuurt, heb je geen probleem — omdat dat geen browsers zijn en ze de same-origin policy niet handhaven. Precies daarom verschijnt de fout alleen in de browser.
Wat is een preflight (OPTIONS)-verzoek?
Voor sommige verzoeken stuurt de browser een preflight-verzoek vóór het eigenlijke verzoek. Dit is een "toestemmingscontrole" uitgevoerd met de OPTIONS-methode. De browser vraagt: "Mag ik een verzoek sturen vanaf deze origin, met deze methode, met deze headers?"
Een preflight wordt getriggerd omdat het verzoek niet "simpel" is. Een verzoek heeft een preflight nodig als het een andere methode gebruikt dan GET, POST of HEAD (bijv. PUT, DELETE, PATCH), of als het aangepaste headers bevat (bijv. Authorization, Content-Type: application/json). De server moet de toegestane methoden en headers teruggeven in zijn OPTIONS-antwoord:
Access-Control-Allow-Origin: https://site.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
Access-Control-Max-Age vertelt de browser hoeveel seconden het preflight-antwoord in de cache moet blijven, zodat niet bij elk verzoek opnieuw een OPTIONS-aanroep nodig is. Als de server een 404 of 405 teruggeeft op het OPTIONS-verzoek, is dat het eerste wat je moet oplossen.
De CORS-fout oplossen aan de serverkant
De oplossing verschilt per framework, maar de logica is hetzelfde: voeg de juiste headers toe. In een op Express gebaseerde Node.js-API is het officiële cors-pakket de schoonste weg:
const cors = require('cors');
app.use(cors({
origin: 'https://site.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
}));
In Laravel staat de CORS-configuratie in config/cors.php en past de HandleCors-middleware deze automatisch toe. Daar definieer je de toegestane origins:
'paths' => ['api/*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['https://site.com'],
'allowed_headers' => ['*'],
'supports_credentials' => true,
Op een webserver zoals Nginx of Apache kun je de headers ook rechtstreeks toevoegen, maar ze op de applicatielaag beheren is doorgaans flexibeler omdat je de origin voorwaardelijk kunt valideren.
Werken met inloggegevens (cookies en tokens)
Als je verzoeken cookies of authenticatiegegevens meedragen, treden er twee regels in werking. Ten eerste: je moet credentials: 'include' aan de clientkant inschakelen:
fetch('https://api.site.com/data', {
credentials: 'include',
});
Ten tweede en het meest cruciaal: wanneer inloggegevens worden gebruikt, kan de Access-Control-Allow-Origin-header niet * (wildcard) zijn. De browser weigert dit om veiligheidsredenen; je moet de exacte origin opgeven. De server moet ook Access-Control-Allow-Credentials: true sturen. Deze twee regels overslaan is een van de meest voorkomende oorzaken van de verwarrende fout "wildcard kan niet met inloggegevens worden gebruikt".
Veelgemaakte fouten en een snelle checklist
- Wildcard + inloggegevens-conflict: als je cookies stuurt, gebruik dan de exacte origin in plaats van
*. - OPTIONS blijft onbeantwoord: zorg dat je server een 2xx-antwoord op de preflight teruggeeft.
- Verschil in trailing slash:
https://site.comversushttps://site.com/kan de origin-match breken; vergelijk protocol + host + poort nauwkeurig. - Proxy-omweg: in ontwikkeling kun je de frontend-devserver (bijv. Vite) naar de API proxyen, zodat verzoeken lijken te komen van dezelfde origin.
Veelgestelde vragen
Kan ik de CORS-fout oplossen door alleen de frontendcode te wijzigen?
Nee. De juiste response-headers moeten van de server komen. Het enige wat je aan de frontend kunt doen is een proxy gebruiken in ontwikkeling of je credentials-instelling corrigeren; de echte toestemming wordt altijd aan de API-kant gegeven.
Waarom werkt het in Postman maar krijg ik een CORS-fout in de browser?
Omdat CORS een beveiligingsbeleid is dat alleen door de browser wordt gehandhaafd. Postman en curl handhaven de same-origin policy niet, dus hetzelfde verzoek werkt daar prima. Dit vertelt je dat de server draait maar dat de CORS-headers ontbreken.
Is het veilig om Access-Control-Allow-Origin: * te gebruiken?
Het is acceptabel voor een publieke API zonder inloggegevens. Maar gebruik voor API's die met cookies/tokens werken of gevoelige data leveren, geen wildcard; vermeld de toegestane origins expliciet.
Wil je de CORS-configuratie op je API definitief en veilig oplossen? Laten we samen de origin-, preflight- en inloggegevens-instellingen tussen je frontend en backend doornemen en een oplossing bouwen die bij je project past — neem contact met me op.