CORS, en waarom je het steeds tegenkomt
De foutmelding die iedereen wegklikt met een regel van internet, en wat die regel echt uitzet.
- SoortStappenplan
- Tijd15 minuten
- Voor wieIedereen die weleens een fout over Access-Control-Allow-Origin heeft gezien
- Fases4
Op enig moment krijg je deze in je console: has been blocked by CORS policy, no Access-Control-Allow-Origin header is present. Je vraagt het aan een AI, die zegt zet de origin op een sterretje, het werkt weer, en je gaat door. Dat sterretje is precies het probleem, want CORS is geen slot dat jou tegenhoudt maar een sleutel die jij uitdeelt.
Deze handleiding is geschreven om als werkopdracht te dienen. Neem hem in zijn geheel mee naar Cursor, Claude Code of waar je ook bouwt, en werk hem fase voor fase af.
Waar het misgaat
Fase 0Begrijpen wat je uitzet
Twee alinea's, en daarna snap je waarom het sterretje geen oplossing is.
Browsers hebben een oude, harde regel: JavaScript op site A mag niet zomaar de antwoorden lezen van site B. Zonder die regel zou een willekeurige site die je in een ander tabblad opent jouw ingelogde webmail kunnen uitlezen, want je cookies gaan gewoon mee.
CORS is de uitzondering daarop. Het is de manier waarop server B tegen de browser zegt: van site A mag je dit wél lezen. Zet je daar een sterretje neer, dan zeg je: van iedereen.
Fase 1Het sterretje vervangen
Een vaste lijst met adressen die jij kent, en geen regex waar per ongeluk van alles doorheen glipt.
import cors from 'cors';
// Een vaste lijst met adressen die jij kent. Geen sterretje, en geen regex
// waar per ongeluk mijnsite.nl.kwaadaardig.com doorheen glipt.
const TOEGESTAAN = [
'https://jouwsite.nl',
'https://www.jouwsite.nl',
...(process.env.NODE_ENV === 'development' ? ['http://localhost:5173'] : [])
];
app.use(cors({
origin(herkomst, klaar) {
// Geen herkomst betekent: geen browser (curl, een server, een mobiele app).
// Die laat je door of niet, maar CORS is daar sowieso je beveiliging niet.
if (!herkomst) return klaar(null, true);
if (TOEGESTAAN.includes(herkomst)) return klaar(null, true);
return klaar(new Error('niet toegestaan door CORS'));
},
credentials: true, // nodig als je cookies meestuurt
methods: ['GET', 'POST', 'PUT', 'DELETE'],
maxAge: 86400 // scheelt een preflight-verzoek per dag
}));
Fase 2Als de fout blijft
Vier symptomen en waar je fout dan echt zit.
| Wat je ziet | Wat het meestal is |
|---|---|
| Fout verdwijnt niet na aanpassen | Je preflight wordt gecachet, of je bewerkt de verkeerde omgeving |
| Werkt lokaal, niet live | localhost staat in je lijst en je echte domein niet, of www ontbreekt |
| Alleen bij POST, niet bij GET | De preflight met OPTIONS wordt niet beantwoord door je router |
| Cookies komen niet aan | credentials ontbreekt aan de fetch-kant, of SameSite staat op Lax |
Fase 3Controleren
Bewijs voordat je zegt dat het klaar is. Twee commando's, geen browser nodig.
# Een toegestaan adres: hier hoort je eigen domein terug te komen. curl -s -I -H "Origin: https://jouwsite.nl" https://api.jouwsite.nl/iets \ | grep -i access-control-allow-origin # Een adres dat er niet in staat: hier hoort GEEN header terug te komen. curl -s -I -H "Origin: https://kwaadaardig.example" https://api.jouwsite.nl/iets \ | grep -i access-control-allow-origin # De preflight, voor een POST met een eigen header: curl -s -I -X OPTIONS \ -H "Origin: https://jouwsite.nl" \ -H "Access-Control-Request-Method: POST" \ https://api.jouwsite.nl/iets
- Je eigen domein krijgt de header terugZonder www én met www, want dat zijn voor de browser twee verschillende adressen.
- Een onbekend adres krijgt géén header terugKrijg je hem wel, of staat er een sterretje, dan staat je lijst nog niet aan.
- De preflight geeft een 200 of 204Een 404 op OPTIONS betekent dat je router de preflight niet afhandelt, en dan faalt elke POST.
- Er staat nergens meer origin: '*' in je codeZoek er letterlijk op, ook in je serverless-functies en je middleware.
Alles hierboven in één keer, inclusief de fases, de SQL en de controlestap.
Werkt dit, maar wil je het niet zelf doen?
Dit blad is gratis en blijft gratis. Wil je dat het bij jou automatisch draait in plaats van met de hand, dan bouw ik dat. Beschrijf in vijf vragen wat er vastloopt en stuur het adres van je app mee; binnen één werkdag heb je mijn antwoord op papier.
Vertel wat er vastloopt