Wat is CORS? Uitleg, CORS-meldingen oplossen en headers instellen
Gepubliceerd op 9 november 2026 11 min leestijd
CORS bepaalt welke websites de antwoorden van je server mogen lezen. Lees hoe het werkt, wat een CORS-melding betekent en hoe je CORS toestaat.
CORS (Cross-Origin Resource Sharing) is een regeling in je browser waarmee een server aangeeft welke andere websites zijn antwoorden mogen ophalen en lezen. Standaard mag een script op de ene website geen gegevens lezen van een ander domein; met CORS-headers zoals Access-Control-Allow-Origin geeft de server daar toestemming voor. Zie je een CORS-melding, dan stuurde de server die toestemming niet (goed) mee, en los je het op bij de server die de gegevens levert, niet in de pagina die erom vraagt.
Wat is CORS?
Elke webpagina heeft een origin: de combinatie van protocol (https), hostnaam (www.jouwdomein.nl) en poort. Browsers houden websites met een verschillende origin van elkaar gescheiden. Die regel heet de same-origin policy. Een verzoek van de ene origin naar een andere heet cross-origin, en CORS is de afspraak waarmee een server zo'n verzoek toch toestaat.
Dat de origin uit drie delen bestaat, maakt verschil. Zie hoe de browser andere adressen beoordeelt vanaf een pagina op https://www.jouwdomein.nl:
| Adres | Zelfde origin? | Waarom |
|---|---|---|
https://www.jouwdomein.nl/contact | Ja | Alleen het pad is anders |
http://www.jouwdomein.nl | Nee | Ander protocol (http in plaats van https) |
https://jouwdomein.nl | Nee | Andere hostnaam: zonder www |
https://shop.jouwdomein.nl | Nee | Andere hostnaam: een subdomein |
https://www.jouwdomein.nl:8443 | Nee | Andere poort |
https://api.andere-dienst.nl | Nee | Ander domein |
Een eigen subdomein of de versie van je site zonder www telt voor de browser dus als een andere website. Daar komen veel CORS-problemen vandaan.
Waarom blokkeert de browser verzoeken naar een ander domein?
De same-origin policy beschermt jou als bezoeker. Ben je ingelogd bij je webmail of je bank, dan stuurt je browser bij elk verzoek naar die site je inlogcookie mee. Zonder deze regel zou een script op een willekeurige andere website op de achtergrond je mail of saldo kunnen opvragen en uitlezen. De browser laat zo'n script het antwoord daarom niet lezen, tenzij de server met CORS-headers zegt dat het mag.
Belangrijk om te weten: CORS is een regel van de browser, geen slot op je server. Programma's als curl, een app of een andere server trekken zich niets van CORS aan, en bij een GET- of POST-verzoek zonder preflight komt het verzoek vaak wel bij je server aan; de browser houdt alleen het antwoord tegen. Je server moet dus zelf blijven controleren wie er ingelogd is en wat iemand mag.
CORS geldt niet voor alles wat een pagina laadt. Deze verzoeken vallen eronder:
- verzoeken uit JavaScript met
fetch()ofXMLHttpRequest, bijvoorbeeld naar een API; - webfonts die je in CSS laadt met
@font-face; - JavaScript-modules (
<script type="module">); - afbeeldingen en video die je in een
canvasof met WebGL bewerkt, en elementen met het attribuutcrossorigin.
Een afbeelding met <img>, een stylesheet of een script met src naar een ander domein laadt zonder CORS. Ook een link, een formulier of een iframe met een video van YouTube of Vimeo heeft geen CORS-headers nodig.
Hoe werkt CORS?
Bij een verzoek zonder preflight, zoals het ophalen van gegevens met GET, gaat het zo:
- Een script op
https://www.jouwdomein.nlvraagt gegevens op bijhttps://api.andere-dienst.nl. - De browser zet daar de header
Origin: https://www.jouwdomein.nlbij. - De server stuurt het antwoord terug, met
Access-Control-Allow-Origin: https://www.jouwdomein.nlals hij jouw site toestaat (of*voor iedereen). - De browser vergelijkt die waarde met de origin van de pagina. Komen ze overeen, dan krijgt het script het antwoord. Zo niet, dan houdt de browser het tegen en zet hij een CORS-melding in de console.
Preflight: het OPTIONS-verzoek vooraf
Voor verzoeken die meer kunnen aanrichten, vraagt de browser eerst toestemming met een apart preflight-verzoek. Dat gebeurt onder andere bij de methoden PUT, PATCH en DELETE, bij eigen headers zoals Authorization of X-API-Key, en bij een POST met Content-Type: application/json. Alleen GET, HEAD en POST met een formulier- of tekstformaat (application/x-www-form-urlencoded, multipart/form-data of text/plain) en standaardheaders gaan zonder preflight.
De browser stuurt dan eerst een OPTIONS-verzoek:
OPTIONS /bestellingen HTTP/1.1
Host: api.andere-dienst.nl
Origin: https://www.jouwdomein.nl
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type, authorization
De server moet daarop antwoorden met een geslaagde status (200 of 204) en de headers die het verzoek toestaan:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://www.jouwdomein.nl
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 7200
Pas daarna verstuurt de browser het echte verzoek. Met Access-Control-Max-Age onthoudt de browser de toestemming een tijdje, zodat niet elk verzoek een preflight nodig heeft. Zonder die header is dat 5 seconden; Chrome onthoudt het hooguit 2 uur (7200 seconden), Firefox hooguit 24 uur.
De CORS-headers op een rij
| Header | Wat hij regelt | Voorbeeld |
|---|---|---|
Access-Control-Allow-Origin | Welke origin het antwoord mag lezen: precies één origin, of * voor iedereen | https://www.jouwdomein.nl |
Access-Control-Allow-Methods | Welke methoden zijn toegestaan (antwoord op de preflight) | GET, POST, PUT |
Access-Control-Allow-Headers | Welke eigen headers het verzoek mag meesturen (antwoord op de preflight) | Content-Type, Authorization |
Access-Control-Allow-Credentials | Of cookies en inloggegevens mee mogen | true |
Access-Control-Max-Age | Hoe lang de browser de preflight onthoudt, in seconden | 7200 |
Access-Control-Expose-Headers | Welke extra headers van het antwoord het script mag lezen | X-Total-Count |
Vary | Vertelt caches dat het antwoord per origin verschilt | Origin |
Twee regels om te onthouden. Access-Control-Allow-Origin bevat altijd één waarde: een lijst met domeinen werkt niet. Wil je meerdere sites toelaten, dan kiest je server per verzoek de juiste origin (zie het PHP-voorbeeld verderop). En gaan er cookies of inloggegevens mee, dan moet de server een echte origin noemen in plaats van * en Access-Control-Allow-Credentials: true sturen.
Wat betekent een CORS-melding?
Een CORS-melding (vaak "CORS policy error" genoemd) betekent dat de browser een antwoord heeft tegengehouden omdat de server geen passende toestemming meestuurde. Je script krijgt alleen een algemene netwerkfout te zien; de echte reden staat om veiligheidsredenen alleen in de ontwikkelaarstools van je browser. Zo vind je hem:
- Open de pagina en druk op F12 (op een Mac Cmd+Option+I).
- Kijk in het tabblad Console. Daar staat de melding met de reden.
- Kijk in het tabblad Netwerk (Network) en herlaad de pagina. In Chrome staat bij het verzoek in de kolom Status CORS-fout (CORS error). Het OPTIONS-verzoek vooraf staat als aparte regel in de lijst, met in de kolom Type Pre-flight (preflight).
In Chrome begint de melding altijd zo: Access to fetch at '…' from origin '…' has been blocked by CORS policy:, gevolgd door de reden. Firefox schrijft Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at … met de reden tussen haakjes achter Reason:. Het eerste adres is het verzoek dat mislukte, het tweede de origin van jouw pagina.
Veelvoorkomende CORS-problemen en de oplossing
De reden achter de melding vertelt je precies waar het zit. Dit zijn de meldingen van Chrome die je het vaakst ziet:
| Melding (Chrome) | Oorzaak | Oplossing |
|---|---|---|
No 'Access-Control-Allow-Origin' header is present on the requested resource. | De server stuurt geen CORS-header mee. Ook een foutpagina, een firewall of een doorverwijzing zonder de header geeft deze melding. | Zet de header op de server die de gegevens levert, en controleer of ook foutantwoorden hem meesturen. |
The 'Access-Control-Allow-Origin' header has a value '…' that is not equal to the supplied origin. | De header noemt een andere origin, vaak de versie met of zonder www, of http in plaats van https. | Noem de origin precies zoals hij in de melding staat, inclusief https en www. |
The 'Access-Control-Allow-Origin' header contains multiple values '…', but only one is allowed. | De header staat op twee plekken (bijvoorbeeld in .htaccess én in een plugin of in je code), of er staat een lijst met domeinen in. | Stel de header op één plek in. Voor meerdere sites kiest je code per verzoek één origin. |
The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. | Het verzoek stuurt cookies of inloggegevens mee, terwijl de server * antwoordt. | Antwoord met de echte origin en stuur Access-Control-Allow-Credentials: true mee. |
Response to preflight request doesn't pass access control check: It does not have HTTP ok status. | Het OPTIONS-verzoek krijgt een fout, zoals 401, 403, 404 of 500. Vaak vraagt de server ook voor OPTIONS om in te loggen, of blokkeert een beveiligingsplugin het verzoek. | Laat OPTIONS zonder inloggen door en antwoord met 200 of 204 en de CORS-headers. |
Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request. | Het adres stuurt door, bijvoorbeeld van http naar https, van zonder www naar met www, of naar een adres met een slash aan het eind. | Gebruik in je code het eindadres, precies zoals het in de adresbalk staat na de doorverwijzing. |
Request header field … is not allowed by Access-Control-Allow-Headers in preflight response. | Je stuurt een header mee die de server niet toestaat, zoals Authorization. | Zet die header in Access-Control-Allow-Headers. |
Method … is not allowed by Access-Control-Allow-Methods in preflight response. | De methode, bijvoorbeeld PUT of DELETE, staat niet in de lijst van de server. | Voeg de methode toe aan Access-Control-Allow-Methods. |
Open je een HTML-bestand rechtstreeks vanaf je computer (een adres dat met file:// begint), dan is de origin null en mislukken bijna alle verzoeken met CORS. Test je pagina daarom via een lokale webserver of op je hosting.
De oplossing zit altijd bij de server die de gegevens levert. Een browserextensie die CORS uitzet werkt alleen in jouw eigen browser, en mode: 'no-cors' in fetch() geeft je een afgeschermd (opaque) antwoord waarvan je script de inhoud en headers niet kan lezen. Is de API van een ander en kun je de headers niet laten aanpassen, haal de gegevens dan op via je eigen server, bijvoorbeeld met PHP: tussen twee servers speelt CORS geen rol.
CORS toestaan op je hosting
Ben jij de eigenaar van de gegevens die een andere site wil lezen, dan stel je de CORS-headers in op je eigen website. Bij hosting van LJPc kan dat op twee plekken: in het bestand .htaccess of in je PHP-code. Welke het beste past, hangt af van wat je deelt en van de webserver die het bestand uitlevert.
Controleer eerst welke server je bestand uitlevert
Vraag de headers van het bestand op met curl en geef een origin mee, zoals de browser dat doet:
curl -I -H "Origin: https://www.jouwdomein.nl" https://jouwdomein.nl/fonts/huisletter.woff2
Kijk in de uitvoer naar twee regels:
server: LiteSpeed: de headers uit .htaccess gelden voor alle bestanden, ook voor lettertypen en afbeeldingen.server: nginx: op deze servers levert nginx statische bestanden zoals afbeeldingen, lettertypen, CSS en JavaScript zelf uit, en daar komt een header uit .htaccess niet bij mee. Bij pagina's en antwoorden die PHP maakt, zoals WordPress of je eigen API, komt hij wel mee. Moet een lettertype of afbeelding op zo'n server een CORS-header krijgen, maak dan een supportticket aan met het adres van het bestand en de site die het laadt, dan kijken we met je mee.
Staat access-control-allow-origin in de uitvoer, dan werkt je instelling.
Lettertypen en afbeeldingen delen via .htaccess
Een veelvoorkomend geval: je site draait op www.jouwdomein.nl, maar de CSS laadt de lettertypen van jouwdomein.nl of van een subdomein. De browser ziet dat als een andere origin en weigert het lettertype. De netste oplossing is een relatief adres in je CSS (zoals /fonts/huisletter.woff2), zodat het lettertype van dezelfde origin komt. Lukt dat niet, zet dan een CORS-header op de lettertypen:
- Open het bestand .htaccess in de map httpdocs, zoals beschreven in .htaccess: redirects, beveiliging en meer instellen, en maak eerst een kopie.
- Zet dit blok bovenaan het bestand en sla het op:
<IfModule mod_headers.c>
<FilesMatch "\.(woff2?|ttf|otf|eot)$">
Header set Access-Control-Allow-Origin "*"
</FilesMatch>
</IfModule>
- Controleer het resultaat met het curl-commando hierboven.
Een * is prima voor openbare bestanden zoals lettertypen en afbeeldingen. Mag alleen jouw eigen site ze gebruiken, vervang * dan door één origin, bijvoorbeeld "https://www.jouwdomein.nl". Voeg afbeeldingen toe door hun extensies in de regel met FilesMatch op te nemen, zoals png|jpe?g|webp|svg. Andere headers in .htaccess, zoals beveiligingsheaders, lees je in HTTP-beveiligingsheaders instellen.
CORS in PHP voor je eigen API
Wil je dat meerdere sites je API mogen gebruiken, of moet je ook de preflight beantwoorden, regel het dan in PHP. Dit voorbeeld laat alleen de origins uit de lijst toe en beantwoordt het OPTIONS-verzoek meteen:
<?php
$toegestaan = ['https://www.jouwdomein.nl', 'https://app.jouwdomein.nl'];
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
header('Vary: Origin');
if (in_array($origin, $toegestaan, true)) {
header('Access-Control-Allow-Origin: ' . $origin);
}
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
header('Access-Control-Allow-Methods: GET, POST');
header('Access-Control-Allow-Headers: Content-Type, Authorization');
header('Access-Control-Max-Age: 7200');
http_response_code(204);
exit;
}
// Hierna volgt je eigen code, bijvoorbeeld:
header('Content-Type: application/json');
echo json_encode(['status' => 'ok']);
Zet dit bovenaan het PHP-bestand van je API, vóór er iets wordt uitgevoerd. De regel Vary: Origin zorgt dat caches het antwoord per origin apart bewaren. Gebruiken de andere sites cookies of een inlog, voeg dan binnen het blok met in_array de regel header('Access-Control-Allow-Credentials: true'); toe. Stuur nooit de origin van elke willekeurige site terug in combinatie met credentials, want dan kan elke website de gegevens van je ingelogde bezoekers lezen.
CORS en WordPress
De REST API van WordPress (alles onder /wp-json/) stuurt zelf al CORS-headers mee: WordPress noemt de origin van het verzoek in Access-Control-Allow-Origin en staat de gangbare methoden en headers toe. Voor ingelogde acties vraagt WordPress daarnaast een geldige nonce (de header X-WP-Nonce); zonder nonce behandelt het een verzoek als niet ingelogd. Krijg je toch een CORS-melding bij de REST API, dan zit de oorzaak meestal hier:
- een beveiligingsplugin die de REST API of OPTIONS-verzoeken blokkeert (de preflight krijgt dan 401 of 403);
- een doorverwijzing, omdat je code een adres met http of zonder www gebruikt;
- een tweede CORS-header uit .htaccess of een plugin, naast die van WordPress zelf.
Laat de headers voor de REST API daarom over aan WordPress en zet ze niet nog eens in .htaccess. Laadt je site na een overstap naar https ook onderdelen via http, kijk dan in mixed content en HTTPS-fouten oplossen.
Iframes en embedcodes: wat betekent referrerpolicy?
Plak je de embedcode van een video van YouTube of Vimeo op je site, dan zie je attributen als referrerpolicy="strict-origin-when-cross-origin" en allow="autoplay; fullscreen; picture-in-picture; clipboard-write; encrypted-media; web-share". Dat heeft niets met CORS te maken: de video speelt in een eigen kader van de videodienst, en je pagina hoeft daar niets uit te lezen. De attributen betekenen het volgende:
referrerpolicy="strict-origin-when-cross-origin"bepaalt hoeveel van het adres van jouw pagina de videodienst te zien krijgt. Naar een andere site gaat alleen je origin mee (bijvoorbeeldhttps://www.jouwdomein.nl/), zonder het pad, en naar een onbeveiligde http-site helemaal niets. Het is de standaard in browsers, en YouTube raadt deze waarde aan omdat de speler moet weten op welke site hij staat. Laat hem dus staan. Meer over deze instelling lees je bij Referrer-Policy in HTTP-beveiligingsheaders instellen.allowgeeft het kader toestemming voor functies van de browser: automatisch afspelen (autoplay), volledig scherm (fullscreen), beeld-in-beeld (picture-in-picture), kopiëren naar je klembord (clipboard-write), beveiligde video afspelen (encrypted-media) en delen via het deelmenu van je apparaat (web-share).allowfullscreenis de oudere schrijfwijze voor volledig scherm, enframeborder="0"haalt de rand om het kader weg. Dat laatste attribuut is verouderd; in nieuwe code regel je de rand met CSS.
Hulp nodig?
Kom je er niet uit, of wil je weten welke instelling bij jouw site past? Maak een supportticket aan met het adres van de pagina en de melding uit de console, dan kijken we met je mee. Je kunt ons ook bellen op 085 130 6429.
Veelgestelde vragen
Wat betekent CORS?
CORS staat voor Cross-Origin Resource Sharing. Het is een regeling in de browser waarmee een server met headers zoals Access-Control-Allow-Origin aangeeft welke andere websites zijn antwoorden mogen lezen. Zonder die toestemming houdt de browser het antwoord tegen.
Wat is een CORS-melding?
Een CORS-melding (CORS policy error) verschijnt in de console van je browser als een script gegevens van een ander domein wilde lezen en de server daar geen passende toestemming voor meestuurde. In Chrome begint de melding met "has been blocked by CORS policy", gevolgd door de precieze reden.
Hoe zet ik CORS aan voor mijn website?
Je zet de CORS-headers op de server die de gegevens levert. Bij hosting van LJPc kan dat in .htaccess met Header set Access-Control-Allow-Origin, of in PHP met header(). Op een server die zichzelf als nginx meldt, gelden headers uit .htaccess niet voor statische bestanden zoals lettertypen; maak dan een supportticket aan.
Is Access-Control-Allow-Origin: * veilig?
Voor openbare bestanden zoals lettertypen, afbeeldingen of een openbare API wel. Voor gegevens achter een inlog niet: daar noem je de exacte origin en stuur je Access-Control-Allow-Credentials: true mee. Browsers weigeren de wildcard sowieso zodra er cookies of inloggegevens meegaan.
Kan ik een CORS-fout oplossen in mijn eigen JavaScript?
Nee, de toestemming moet van de server komen. mode: no-cors in fetch() levert een afgeschermd antwoord op dat je script niet kan lezen, en een browserextensie werkt alleen in je eigen browser. Kun je de server niet aanpassen, haal de gegevens dan op via je eigen server, want tussen servers geldt CORS niet.
Beschermt CORS mijn website tegen aanvallen?
Niet direct. CORS beschermt bezoekers in hun browser tegen sites die hun gegevens willen uitlezen, maar programma's als curl en andere servers negeren het. Je server moet dus zelf blijven controleren wie er ingelogd is en wat iemand mag.