Ein Request funktioniert in Postman, aber der Browser blockiert ihn mit einer CORS-Meldung. In vielen Teams landet das Thema dann zwischen Frontend, Backend und DevOps, ohne klare Zuständigkeit. Technisch ist CORS jedoch gut greifbar: Der Browser erzwingt Regeln, die festlegen, welche Origins (Schema + Host + Port) eine Ressource anfragen dürfen. Wer die Mechanik versteht, kann Fehler reproduzierbar beheben und nebenbei typische Sicherheitslücken vermeiden.
Warum CORS existiert und was der Browser wirklich prĂĽft
Same-Origin-Policy als Ausgangspunkt
Browser schützen Nutzer:innen seit jeher durch die Same-Origin-Policy: JavaScript aus https://app.example darf nicht einfach Responses von https://api.other lesen. Ohne diese Regel könnten beliebige Seiten im Namen eingeloggter Nutzer Daten abgreifen. CORS (Cross-Origin Resource Sharing) ist keine Aufhebung dieser Policy, sondern ein kontrollierter Mechanismus: Der Server entscheidet per Response-Header, ob eine fremde Origin lesen darf.
Wichtig: CORS ist Browser-Policy, keine Server-Authentifizierung
Ein häufiger Denkfehler: „Wenn CORS blockt, ist die API sicher.“ Das stimmt nicht. CORS verhindert primär, dass JavaScript im Browser die Response lesen kann. Server-zu-Server-Calls, mobile Apps oder CLI-Tools sind davon nicht betroffen. Authentifizierung und Autorisierung müssen separat sauber umgesetzt werden (Tokens, Sessions, Rechteprüfung).
Preflight-Requests: Wann OPTIONS auftaucht und was beantwortet werden muss
Simple Requests vs. „nicht-simple“ Requests
Der Browser versucht es bei manchen Requests zunächst ohne Vorabprüfung. Sobald aber bestimmte Bedingungen erfüllt sind, führt er einen Preflight aus: ein OPTIONS-Request, der klärt, ob der eigentliche Call erlaubt ist. Preflight wird typischerweise ausgelöst durch:
- Methoden wie PUT, PATCH, DELETE (statt GET/POST),
- Custom-Header (z.B. Authorization),
- Content-Type auĂźerhalb der klassischen Form-Varianten (z.B. JSON im Alltag).
Für Teams ist das entscheidend: Nicht nur der „echte“ Endpoint muss CORS-Header liefern, sondern auch die OPTIONS-Antwort (und zwar passend zur konkreten Origin und den angefragten Headern).
Welche Header im Preflight zählen
Beim Preflight sendet der Browser u.a. Access-Control-Request-Method und Access-Control-Request-Headers. Der Server muss darauf u.a. reagieren mit:
- Access-Control-Allow-Origin (konkret oder dynamisch),
- Access-Control-Allow-Methods (enthält die gewünschte Methode),
- Access-Control-Allow-Headers (enthält die gewünschten Header),
- optional Access-Control-Allow-Credentials, wenn Cookies/HTTP-Auth genutzt werden,
- optional Access-Control-Max-Age, um Preflights zu cachen.
Wenn einer dieser Punkte nicht passt, scheitert der eigentliche Request, obwohl der Endpoint „eigentlich“ erreichbar wäre.
CORS korrekt konfigurieren: Patterns, die im Alltag funktionieren
Origin-Whitelisting statt Wildcards
In Produktivsystemen ist eine feste Liste erlaubter Origins meist die wartbarste Option. Typischerweise gibt es mindestens zwei bis drei Umgebungen: lokale Entwicklung, Staging, Produktion. Das Mapping sollte im Backend oder am API-Gateway zentral gepflegt werden, z.B. über Konfigurationswerte. Dynamische Rückgabe von Access-Control-Allow-Origin ist okay, solange nur bekannte Origins gespiegelt werden und nicht „alles, was kommt“.
Cookies, Sessions und „Credentials“ richtig behandeln
Sobald der Browser Cookies mitsendet (z.B. Session-Cookie) oder HTTP-Auth nutzt, gilt eine harte Regel: Access-Control-Allow-Origin darf dann nicht * sein. Zusätzlich muss Access-Control-Allow-Credentials auf true stehen. Im Frontend muss der Request auĂźerdem mit credentials: ‚include‘ (Fetch) bzw. withCredentials (XHR/Axios) abgesetzt werden.
Praktische Folge: Token-basierte Auth (Bearer Token im Authorization-Header) ist oft einfacher für Cross-Origin-Szenarien, weil kein Cookie-Handling und keine SameSite-Spezifika im Spiel sind. Bei Sessions ist CORS dennoch sauber lösbar, verlangt aber konsistente Einstellungen.
Expose-Headers: Wenn das Frontend bestimmte Response-Header braucht
Manche Backends liefern wichtige Informationen in Headern, z.B. Pagination (X-Total-Count) oder Dateinamen (Content-Disposition). Der Browser lässt JavaScript diese Header nur lesen, wenn der Server sie via Access-Control-Expose-Headers freigibt. Ohne das wirkt die Response „vollständig“, aber im Code fehlen die Headerwerte.
Typische Fehlerbilder in Produktion: Reverse Proxies, Gateways, CDN
Header gehen unterwegs verloren oder werden doppelt gesetzt
In realen Setups liegen häufig mehrere Schichten zwischen Browser und App: CDN, WAF, API-Gateway, Reverse Proxy, Service Mesh. CORS kann an jeder Stelle brechen, wenn:
- OPTIONS nicht bis zum Backend durchgeroutet wird,
- eine Schicht CORS-Header entfernt,
- Header mehrfach gesetzt werden (Browser bewertet am Ende das Ergebnis, nicht die Absicht).
Ein robustes Pattern ist: CORS genau an einer Schicht terminieren (z.B. Gateway), dort auch OPTIONS beantworten und im Backend CORS deaktivieren oder minimal halten. Alternativ kann es im Backend liegen, dann müssen Proxy-Regeln OPTIONS zuverlässig weiterleiten.
Cache-Probleme: Vary: Origin nicht vergessen
Wenn Access-Control-Allow-Origin dynamisch pro Origin gesetzt wird, muss die Response fĂĽr Caches unterscheidbar sein. Sonst kann ein Cache die Response samt falschem Allow-Origin an eine andere Origin ausliefern. Das fĂĽhrt zu schwer reproduzierbaren Fehlern. Technisch hilft ein korrektes Vary: Origin in den Responses, die dynamisch variieren.
Debugging-Ablauf, der in Teams funktioniert
Ein kurzer, reproduzierbarer PrĂĽfpfad
- Im Browser-Devtools „Network“ öffnen und prüfen, ob vor dem eigentlichen Request ein OPTIONS-Preflight läuft.
- Beim OPTIONS-Request Response-Header kontrollieren: Origin, Methods, Headers, Statuscode (2xx/204).
- Beim eigentlichen Request prüfen, ob die Response ebenfalls den passenden Allow-Origin-Header trägt.
- Bei Credentials prĂĽfen: Frontend sendet Credentials aktiv, Server erlaubt Credentials und nutzt keine Wildcard-Origin.
- Wenn ein Proxy/Gateway davor liegt: OPTIONS-Routing und Header-Passthrough in dieser Schicht ĂĽberprĂĽfen (ein Schritt nach dem anderen).
Pragmatische Tests ohne „Browser-Magie“
FĂĽr eine klare Diagnose kann ein Preflight auch manuell nachgebildet werden, z.B. mit curl. Entscheidend ist, die gleichen Header zu senden wie der Browser: Origin, Access-Control-Request-Method, Access-Control-Request-Headers. So wird sichtbar, ob die Infrastruktur korrekt antwortet oder ob ein Zwischenhop (Proxy/CDN) umkonfiguriert ist.
Implementierungsnotizen aus Backend-Sicht (Framework-agnostisch)
OPTIONS-Handling gehört zur API-Oberfläche
In vielen Codebases fehlt eine explizite Behandlung von OPTIONS, weil die Business-Logik nur GET/POST etc. abdeckt. Für CORS ist OPTIONS jedoch Teil der öffentlichen Schnittstelle. Das Handling sollte daher wie ein „First-Class“-Endpoint behandelt werden: klare Regeln, Logging, und Tests, die sicherstellen, dass neue Routen nicht plötzlich ohne Preflight-Antwort deployt werden.
Tests: CORS als Contract prĂĽfen
Statt CORS nur manuell im Browser zu testen, hilft ein kleiner Satz automatisierter Contract-Tests gegen die laufende API (z.B. in Staging): Preflight auf kritische Endpoints, Response-Header prüfen, ggf. mit und ohne Credentials. Das reduziert „funktioniert lokal“-Überraschungen und macht Regressionen bei Proxy- oder Gateway-Änderungen sichtbar.
Einordnung: CORS im Zusammenspiel mit API-Design und Betrieb
Grenzen sauber ziehen: Backend, Gateway, Frontend
CORS ist am stabilsten, wenn Ownership klar ist. In vielen Teams liegt die Verantwortung am Gateway, weil dort ohnehin Routing, TLS und Security-Header zusammenlaufen. In anderen Setups ist das Backend zuständig, weil dort die Origin-Logik (z.B. Mandanten) bekannt ist. Wichtig ist weniger die „richtige“ Schicht, sondern die Konsistenz: eine Stelle definiert Regeln, andere Stellen fummeln nicht unkoordiniert an Headern.
Interne Links fĂĽr angrenzende Themen
Wer bei CORS über „Frontend vs. Backend“ diskutiert, landet schnell bei weiteren Robustheits-Themen: Für saubere Fehlerbehandlung bei externen Abhängigkeiten kann ein Circuit Breaker im Backend helfen. Wenn Auth über Tokens läuft, ist ein sauberer Umgang mit JWT-Auth im Backend relevant. Und wer API-Grenzen schärfen möchte, vermeidet viele Client-Fehler durch Schema-Validation für APIs.
Bei stabiler CORS-Konfiguration entsteht ein positiver Nebeneffekt: Die API-Oberfläche wird bewusster gepflegt. Preflight-Handling, erlaubte Methoden und Header sind dann nicht mehr „Browser-Kram“, sondern ein klarer Teil des Schnittstellenvertrags zwischen Clients und Backend.
