“Toen was één bestand genoeg…”
De PHP waarmee ik tijdens mijn studie werkte, was echt comfortabel. In één enkel index.php-bestand kon je HTML-tags zetten, daartussen <?php ?> openen en daar meteen je code voor database-opvragingen schrijven. Er was geen duidelijke scheiding tussen frontend en backend. Zodra je een variabele declareerde, kon je die overal in de HTML gebruiken, en fouten door datacommunicatie kwamen bijna niet voor.
Maar toen ik in de praktijk overstapte op een stack met Vue.js aan de frontendkant en Spring Boot aan de backendkant, begon de hel.
“De Spring-server draait lokaal toch gewoon, en het Vue-scherm ook, dus waarom komt de data niet door?”
De console van Chrome DevTools stond vol felrode foutmeldingen.
Access to XMLHttpRequest at ... has been blocked by CORS policy
Wat is dat CORS eigenlijk dat mijn data tegenhoudt? En waarom hebben we die makkelijke methode van vroeger, waarbij één bestand genoeg was, opgegeven om frontend en backend expres uit elkaar te trekken?

Wie kookt er eigenlijk? (SSR vs CSR)
Om deze verandering te begrijpen, moet je eerst begrijpen wie het “gerecht” maakt dat een webpagina, dus HTML, is. Precies daarin zit het verschil tussen SSR (Server Side Rendering) en CSR (Client Side Rendering).
1. SSR: een kant-en-klare lunchbox (PHP, JSP)
2. CSR: een bezorgde meal kit (React, Vue)
[Tip] CSR en SPA zijn niet exact hetzelfde
De termen worden vaak door elkaar gebruikt, maar strikt genomen betekenen ze niet precies hetzelfde.
Meestal gebruiken we CSR om een SPA te bouwen. Met andere woorden: omdat we paginageflikker willen vermijden door één pagina te gebruiken (SPA), kiezen we voor een aanpak waarbij de browser zelf de nodige delen opnieuw tekent (CSR).
We gebruiken Vue.js of React vanwege de gebruikerservaring. Om een website net zo vloeiend te laten werken als een smartphone-app, hebben we het “koken” verplaatst van de server naar de browser.

CORS, de beveiliger die ongewenste gasten tegenhoudt
Doordat frontend (Vue) en backend (Spring) van elkaar zijn gescheiden, hebben we in feite “twee huizen” gekregen.
En daar ontstaat het probleem. Om veiligheidsredenen hanteert de browser een basisregel: “vertrouw resources van een andere Origin niet zomaar.” Dat is SOP, ofwel Same Origin Policy.
Stel je dit eens voor. Ik ben ingelogd op Naver, en vanaf een site van een hacker wordt in het geheim een verzoek naar de Naver-server gestuurd met de boodschap: “geef mij de persoonlijke informatie van deze gebruiker.” Als de browser dat niet zou blokkeren, lagen al mijn gegevens open en bloot.
Daarom blokkeert de browser standaard verzoeken zodra de origin, dus domein of poort, verschilt. Maar voor de ontwikkeling hebben wij die poorten juist expres uit elkaar gehaald. We willen alleen onze eigen data gebruiken, en toch zegt de browser, als een beveiliger: “Jouw poort klopt niet. Toegang geweigerd.” Dat is de ware aard van een CORS-fout, Cross-Origin Resource Sharing.
[Code Verification] Een toegangspas uitgeven
De oplossing is eigenlijk simpel. De server, dus de backend, moet alleen tegen de browser, de beveiliger, zeggen: “Deze persoon is een gast die ik heb uitgenodigd. Laat hem binnen.” Met andere woorden: geef hem een toegangspas.
In Spring Boot kun je die toegangspas met één configuratiebestand uitgeven.
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 1. Voor elk API-pad
.allowedOrigins("http://localhost:8080") // 2. Sta verzoeken van dit adres toe
.allowedMethods("GET", "POST", "PUT", "DELETE") // 3. Sta deze typen verzoeken toe
.allowCredentials(true); // 4. Cookies en authenticatie mogen meegestuurd worden
}
}
Analyse:
Let wel: uit gemakzucht moet je niet alles openzetten met allowedOrigins("*"). Dat is alsof je de voordeur wagenwijd open laat staan met een bordje: “Welkom, dieven.”
Praktisch advies: wanneer gebruik je wat?
Betekent dat dan dat CSR, dus Vue of React, altijd het juiste antwoord is? Nee. In de praktijk moet je kiezen op basis van het doel.
Tot slot: voorbij de grens
De oude PHP-tijd, als “grote familie onder één dak”, was comfortabel, maar hoe complexer alles werd, hoe moeilijker het werd om te beheren. Het huidige model met “twee afgesplitste gezinnen”, frontend en backend, is lastiger door communicatie en CORS, maar zorgt er wel voor dat iedere kant zich op zijn eigen rol kan richten.
Dus als je de volgende keer een CORS-fout tegenkomt, probeer dan niet alleen geïrriteerd te raken. De browser pest je niet. Hij voert gewoon een strenge controle uit om jouw huis, dus jouw server, te beschermen.
Nu is de doorgang tussen frontend en backend open. Data kan heen en weer. Maar waar wordt die data eigenlijk op de server opgeslagen? Vroeger kon ik gewoon een query afvuren en ophalen wat ik nodig had. In Spring voelt het door zaken als JPA en Entity ineens alsof je niet eens meer rechtstreeks SQL mag schrijven.
De volgende keer kijken we naar hoe de “schoolboekmanier” van databaseontwerp, normalisatie, in de praktijk juist een blok aan het been kan worden, en waarom JPA zo graag SQL voor je probeert te verbergen.