CORS: was ist das und warum blockiert der Browser eine API | POLPROG Zum Inhalt springen

CORS: was ist das und warum blockiert der Browser eine API

CORS ist kein Mechanismus, der Verbindungen zu einer API einfach blockiert. Es ist ein Browserprotokoll auf Basis der Same-Origin Policy, das entscheidet, ob JavaScript von einem Origin eine Antwort eines anderen Origins lesen darf. In manchen Fällen wird der HTTP-Request gesendet und nur der Zugriff des Skripts auf die Antwort blockiert. In anderen Fällen sendet der Browser zuerst einen OPTIONS-Preflight und entscheidet anhand dessen, ob der eigentliche Request überhaupt gesendet wird.

Veröffentlicht Verfasst von Lesezeit 10 Min. Lesezeit

CORS ist kein Mechanismus, der Verbindungen zu einer API einfach blockiert. Es ist ein Browserprotokoll auf Basis der Same-Origin Policy, das entscheidet, ob JavaScript von einem Origin eine Antwort eines anderen Origins lesen darf. In manchen Fällen wird der HTTP-Request gesendet und nur der Zugriff des Skripts auf die Antwort blockiert. In anderen Fällen sendet der Browser zuerst einen OPTIONS-Preflight und entscheidet anhand dessen, ob der eigentliche Request überhaupt gesendet wird.

Auf dieser Seite
  1. 1CORS beginnt mit der Same-Origin Policy
  2. 2Was ein Origin genau ist
  3. 3Was Browser blockiert API tatsächlich bedeutet
  4. 4Einfache Requests: wann kein Preflight nötig ist
  5. 5Preflight OPTIONS: was geprüft wird
  6. 6Die wichtigsten CORS-Header
  7. 7`Access-Control-Allow-Origin`: Wildcard oder konkreter Origin
  8. 8Cookies und Credentials: häufige Fehlerquelle
  9. 9Warum `mode: "no-cors"` meistens nichts repariert
  10. 10Warum curl, Postman oder Backend funktionieren können
  11. 11CORS ersetzt weder CSRF-Schutz noch Autorisierung
  12. 12Häufige Fehler und Diagnose
  13. 13Gefährliche CORS-Konfigurationen
  14. 14Performance: Preflight-Kosten und `Access-Control-Max-Age`
  15. 15Praktische Konfiguration und Deployment-Checkliste

CORS beginnt mit der Same-Origin Policy

Die Same-Origin Policy ist ein grundlegender Sicherheitsmechanismus des Browsers. Sie beschränkt, wie ein Dokument oder Skript Daten eines anderen Origins lesen kann. Unter anderem verhindert sie, dass eine bösartige Website Daten aus einem Dienst ausliest, bei dem der Nutzer bereits angemeldet ist. [3]

CORS, Cross-Origin Resource Sharing, ist ein HTTP-basierter Mechanismus, mit dem ein Server diese Regel gezielt lockern und erlaubte Origins angeben kann. Der Fetch Standard beschreibt CORS als Opt-in-Protokoll. [1][2]

Was ein Origin genau ist

SeiteZiel-URLBeziehungWarum
https://app.example.comhttps://app.example.com/apiGleicher OriginGleiches Schema, Host und Port
https://app.example.comhttps://api.example.comAnderer OriginAnderer Host
https://app.example.comhttp://app.example.comAnderer OriginAnderes Schema
https://app.example.comhttps://app.example.com:8443Anderer OriginAnderer Port
https://app.example.comhttps://app.example.com/v2Gleicher OriginNur der Pfad ändert sich

Ein Origin wird durch Schema, Host und Port definiert. Zwei URLs sind nur dann same-origin, wenn alle drei Werte übereinstimmen. Der Pfad spielt keine Rolle. [3][4]

`https://app.example.com` und `https://api.example.com` sind daher unterschiedliche Origins. Auch HTTP und HTTPS oder unterschiedliche Ports erzeugen verschiedene Origins. [4]

Same-origin und same-site sind nicht gleichbedeutend. Das ist besonders bei Cookies wichtig, weil `SameSite` auf dem Site-Begriff basiert, CORS dagegen auf Origin. [13]

Was Browser blockiert API tatsächlich bedeutet

Die Aussage, der Browser blockiere den Request, ist oft zu ungenau. Bei einfachen Cross-Origin-Requests kann der Browser den Request senden, eine gültige HTTP-Antwort empfangen und sie anschließend nicht an JavaScript weitergeben, wenn der CORS-Check scheitert. [1][6]

Bei Requests mit Preflight ist der Ablauf anders. Zuerst wird `OPTIONS` gesendet. Schlägt die Prüfung fehl, wird der eigentliche Request nicht gesendet. [2][5][6]

Bei zustandsändernden Operationen ist diese Unterscheidung wichtig. Ein CORS-Fehler bedeutet nicht automatisch, dass das Backend keinen Request erhalten hat.

Einfache Requests: wann kein Preflight nötig ist

MethodeBeispiel-HeaderEinfacher RequestAuswirkung
GETAcceptJaKein Preflight
POSTContent-Type: text/plainJaKein Preflight
POSTContent-Type: application/jsonNeinJSON löst Preflight aus
PUTContent-Type: application/jsonNeinMethode löst Preflight aus
GETAuthorization: Bearer ...NeinHeader löst Preflight aus

Ein Request gilt nur dann als einfach, wenn die CORS-Safelist-Bedingungen erfüllt sind. Zulässige Methoden sind `GET`, `HEAD` und `POST`. Manuell gesetzte Header müssen safelisted sein, und `Content-Type` darf nur `application/x-www-form-urlencoded`, `multipart/form-data` oder `text/plain` verwenden. [1]

Ein typischer `POST` mit `Content-Type: application/json` ist nicht einfach und löst normalerweise einen Preflight aus. Gleiches gilt für `PUT`, `DELETE` oder benutzerdefinierte Header wie `X-Request-ID`. [1][11]

Preflight OPTIONS: was geprüft wird

Ein Preflight ist ein automatisch erzeugter `OPTIONS`-Request. Der Browser sendet `Origin`, `Access-Control-Request-Method` und bei Bedarf `Access-Control-Request-Headers`. Der Server antwortet mit erlaubten Origins, Methoden und Headern. [2][5]

Soll etwa `DELETE` mit `Authorization` gesendet werden, kann der Browser zuerst prüfen, ob der Origin diese Methode und diesen Header verwenden darf. Erst eine positive Antwort erlaubt den eigentlichen Request. [5]

Ein Preflight verursacht einen zusätzlichen Roundtrip. Sein Ergebnis kann in einem speziellen CORS-Preflight-Cache gespeichert werden, getrennt vom normalen HTTP-Cache. [5][10]

Die wichtigsten CORS-Header

HeaderRichtungZweck
OriginRequestOrigin, der den Request initiiert
Access-Control-Allow-OriginResponseOrigin, dessen Code die Antwort erhalten darf
Access-Control-Allow-MethodsPreflight-ResponseVom Preflight erlaubte Methoden
Access-Control-Allow-HeadersPreflight-ResponseVom Preflight erlaubte Request-Header
Access-Control-Allow-CredentialsResponseFreigabe einer Antwort mit Credentials
Access-Control-Expose-HeadersResponseZusätzliche für JavaScript sichtbare Response-Header
Access-Control-Max-AgePreflight-ResponseCache-Dauer des Preflight-Ergebnisses
Vary: OriginResponseTeilt Caches mit, dass die Antwort von Origin abhängt

`Origin` ist ein Request-Header für den initiierenden Origin. `Access-Control-Allow-Origin` ist ein Response-Header des Servers und entscheidet, ob die Antwort mit Code dieses Origins geteilt werden darf. [1][2][8]

`Access-Control-Allow-Methods` und `Access-Control-Allow-Headers` sind vor allem für Preflight wichtig. `Access-Control-Allow-Credentials` erlaubt das Teilen credentialed responses, und `Access-Control-Expose-Headers` kann zusätzliche Response-Header für JavaScript sichtbar machen. [1][2]

Wenn der Server `Access-Control-Allow-Origin` dynamisch abhängig vom Request setzt, empfiehlt MDN zusätzlich `Vary: Origin`. [1]

`Access-Control-Allow-Origin`: Wildcard oder konkreter Origin

`Access-Control-Allow-Origin: *` erlaubt das Teilen einer Antwort mit jedem Origin, solange keine Credentials verwendet werden. Für eine tatsächlich öffentliche API kann das korrekt sein. [2][8]

Für private oder sensible Endpoints sollte die erlaubte Menge möglichst klein sein. MDN und OWASP empfehlen konkrete Origins statt eines bedingungslosen Wildcards, wenn breite Freigabe nicht erforderlich ist. [14][17]

Bei mehreren erlaubten Origins sollte der Server den eingehenden `Origin` gegen eine Allowlist prüfen und nur den akzeptierten Wert zurückgeben. Blindes Spiegeln beliebiger Origins ist unsicher. [8][16]

Cookies und Credentials: häufige Fehlerquelle

Mit `credentials: "include"` kann Fetch Credentials auch bei Cross-Origin-Requests einbeziehen. Das reicht allein nicht. Der Server muss `Access-Control-Allow-Credentials: true` und einen konkreten `Access-Control-Allow-Origin` zurückgeben. `*` ist dann nicht erlaubt. [2][6][9]

Cookies folgen zusätzlich eigenen Regeln. `SameSite` kann verhindern, dass ein Cookie bei einem Cross-Site-Request gesendet wird, und `SameSite=None` erfordert `Secure`. [13]

CORS und `SameSite` lösen unterschiedliche Probleme. CORS kann korrekt sein, während das Cookie fehlt, oder das Cookie kann gesendet werden, während CORS den JavaScript-Zugriff auf die Antwort verhindert.

Warum `mode: "no-cors"` meistens nichts repariert

`fetch(..., { mode: "no-cors" })` ist keine CORS-Umgehung für Anwendungen, die die API-Antwort lesen müssen. Methoden und Header sind eingeschränkt und die Antwort ist opaque. JavaScript kann weder Body noch Header lesen, der sichtbare Status ist `0`. [6][7][11]

`no-cors` hat Spezialfälle, unter anderem bestimmte Service-Worker-Szenarien, ist aber für einen typischen JSON-API-Call meist die falsche Lösung. [6]

Wenn ein fremdes API kein CORS anbietet, kann ein eigener Backend- oder Proxy-Dienst den Request serverseitig ausführen. [11][12]

Warum curl, Postman oder Backend funktionieren können

Same-Origin Policy und CORS werden vom Browser durchgesetzt. Ein HTTP-Client außerhalb dieses Modells unterliegt nicht demselben CORS-Check. Deshalb kann ein Endpoint in curl oder einem API-Tool funktionieren und aus Browser-JavaScript trotzdem blockiert sein. [2][3][16]

Das zeigt auch, warum CORS keine API-Authentifizierung ist. Ein Nicht-Browser-Client kann einen beliebigen `Origin` senden. OWASP warnt daher davor, den Header als Identitätsnachweis zu verwenden. [16]

Das API braucht weiterhin normale Autorisierung, Tokens, Sessions, Berechtigungsprüfungen und serverseitige Validierung.

CORS ersetzt weder CSRF-Schutz noch Autorisierung

Die Same-Origin Policy beschränkt vor allem Cross-Origin-Lesezugriffe. Formulare und bestimmte einfache Requests können trotzdem cross-origin gesendet werden. Cookie-basierte Anwendungen benötigen deshalb weiterhin CSRF-Schutz. [3][15]

OWASP empfiehlt ausdrücklich, CORS oder `Origin` nicht allein als Zugriffskontrolle für sensible Ressourcen zu verwenden. Authentifizierung und Autorisierung bleiben unabhängig davon notwendig. [16]

CORS beantwortet, ob ein Browser eine Antwort an Code eines Origins weitergeben darf. Es beantwortet nicht, ob ein Nutzer oder Client die Operation ausführen darf.

Häufige Fehler und Diagnose

Ein fehlendes `Access-Control-Allow-Origin` ist ein typischer Fehler. Weitere Ursachen sind nicht erlaubte Methoden, fehlende Einträge in `Access-Control-Allow-Headers`, `*` mit Credentials, fehlerhafte OPTIONS-Antworten oder Redirects im CORS-Ablauf. [11][12]

JavaScript erhält absichtlich nur begrenzte Fehlerdetails. Browser-Konsole und Network-Panel sind für die genaue Diagnose wichtiger. [1][11]

`CORS request did not succeed` kann auch durch DNS, Timeout, Verbindungsfehler, TLS, Mixed Content oder Browser-Erweiterungen verursacht werden. [11]

  • Exakten Frontend-Origin prüfen: Schema, Host und Port.
  • `OPTIONS` prüfen, falls vorhanden.
  • Status und `Access-Control-Allow-*` der Preflight-Antwort kontrollieren.
  • Auch die eigentliche Antwort auf CORS-Header prüfen.
  • Bei Cookies `credentials`, `Access-Control-Allow-Credentials`, `SameSite` und `Secure` prüfen.
  • Redirects, TLS, Mixed Content und Netzwerkfehler kontrollieren.
  • Browser-Request mit funktionierendem curl- oder API-Tool-Request vergleichen.

Gefährliche CORS-Konfigurationen

Blindes Spiegeln des eingehenden `Origin` ist gefährlich, wenn credentialed requests sensible Daten liefern. Ein Angreifer kann einen eigenen Origin kontrollieren und versuchen, Antworten im Kontext eines angemeldeten Nutzers zu lesen. [16]

Zu breite reguläre Ausdrücke und Vertrauen in alle Subdomains sind ebenfalls riskant, wenn eine Subdomain übernommen oder von einer anderen Partei kontrolliert werden kann. OWASP empfiehlt exakte Allowlist-Prüfung. [16][17]

`Access-Control-Allow-Origin: *` ist bei vollständig öffentlichen Daten ohne Credentials nicht automatisch eine Schwachstelle. Das Problem ist eine Freigabe, die mehr Daten oder Origins umfasst als beabsichtigt. [8][17]

Performance: Preflight-Kosten und `Access-Control-Max-Age`

Ein Preflight fügt vor dem eigentlichen Request einen zusätzlichen `OPTIONS`-Request hinzu und kann Latenz erhöhen. Das Ergebnis kann mit `Access-Control-Max-Age` im separaten Preflight-Cache gespeichert werden. [5][10]

Ohne Header liegt der Standard laut MDN bei 5 Sekunden. Browser können eigene Obergrenzen setzen. Firefox begrenzt auf 86400 Sekunden, Chromium seit Version 76 auf 7200 Sekunden. [10]

Stabile Policies sollten sinnvoll gecacht werden, aber nicht so lange, dass eine versehentlich zu breite Sicherheitsfreigabe nur schwer schnell zurückgenommen werden kann.

Praktische Konfiguration und Deployment-Checkliste

Zuerst sollte entschieden werden, ob der Endpoint überhaupt cross-origin erreichbar sein muss. Falls nicht, sind keine CORS-Header nötig. Falls ja, sollten konkrete Origins, benötigte Methoden und Header sowie die Notwendigkeit von Credentials definiert werden. [14][17]

Bei dynamischer Allowlist darf nur ein geprüfter Origin zurückgegeben werden, zusammen mit `Vary: Origin`. Bei Credentials sind ein konkreter Origin und `Access-Control-Allow-Credentials: true` erforderlich. Frontend-Code kann diese Serverentscheidung nicht ersetzen. [1][2]

Nach dem Deployment sollten einfacher Request, Preflight, Cookie-Szenario, unerlaubter Origin und echte Fehlerfälle in DevTools getestet werden.

  • Ist Cross-Origin-Zugriff wirklich erforderlich?
  • Welche exakten Origins sind erlaubt?
  • Werden Credentials benötigt?
  • Welche Methoden und Request-Header werden gebraucht?
  • Erreicht `OPTIONS` die Anwendung und antwortet korrekt?
  • Enthält auch die eigentliche Antwort korrekte CORS-Header?
  • Ist bei dynamischem Origin `Vary: Origin` gesetzt?
  • Verlangt der Endpoint weiterhin normale Authentifizierung und Autorisierung?
  • Wurde sowohl ein erlaubter als auch ein unerlaubter Origin getestet?

CORS sollte als Browserregel für den Zugriff auf Antworten verstanden werden und nicht als vollständige API-Sicherheitsgrenze. Zuerst müssen die Origins von Frontend und API bestimmt werden. Danach ist zu prüfen, ob der Request einfach ist oder einen Preflight benötigt. Anschließend werden OPTIONS und die CORS-Header der eigentlichen Antwort kontrolliert. Eine sichere Konfiguration erlaubt nur wirklich benötigte Origins, Methoden und Header, während Authentifizierung und Autorisierung in der Anwendung bleiben.

CORS Same-Origin Policy Web Security HTTP API Fetch API Preflight Cookies CSRF Frontend

Häufig gestellte Fragen

Was ist CORS?

CORS ist ein HTTP-Protokoll, mit dem Browser das Teilen von Antworten zwischen unterschiedlichen Origins kontrollieren. Der Server gibt per Header an, welche Origins eine Antwort lesen dürfen. [1][2]

Warum funktioniert das API in Postman, aber nicht im Browser?

Weil CORS im Browser als Teil der Same-Origin Policy durchgesetzt wird. Externe HTTP-Clients unterliegen diesem Mechanismus nicht. [2][3][16]

Blockiert CORS immer das Senden des Requests?

Nein. Ein einfacher Request kann gesendet werden und nur die Antwort wird vor JavaScript verborgen. Scheitert ein erforderlicher Preflight, wird der eigentliche Request nicht gesendet. [1][5][6]

Was löst einen Preflight aus?

Unter anderem Methoden außerhalb von GET, HEAD, POST, benutzerdefinierte Header und Content-Type: application/json. Der Browser sendet zuerst OPTIONS. [1][5]

Gehört Access-Control-Allow-Origin ins Frontend?

Nein. Es ist ein Server-Response-Header. Frontend-Code kann sich diese Berechtigung nicht selbst geben. [1][8][12]

Repariert mode: "no-cors" CORS?

Nicht für normale APIs, deren Daten gelesen werden müssen. Die Antwort wird opaque und Body, Header sowie normaler Status sind nicht lesbar. [6][7]

Darf Access-Control-Allow-Origin: * verwendet werden?

Ja für öffentliche Antworten ohne Credentials. Für credentialed responses ist * nicht zulässig. [2][8][9]

Wie funktioniert CORS mit Cookies?

Das Frontend verwendet oft credentials: "include", der Server muss einen konkreten Origin und Access-Control-Allow-Credentials: true liefern. Cookies unterliegen zusätzlich SameSite und Secure. [6][9][13]

Schützt CORS das API vor curl oder Bots?

Nein. CORS beschränkt Browser-JavaScript. Ein Nicht-Browser-Client kann eigene Requests und einen eigenen Origin senden, daher bleibt Autorisierung notwendig. [16]

Schützt CORS vor CSRF?

Nicht vollständig. Cookie-basierte Anwendungen brauchen weiterhin CSRF-Schutz. [3][15][16]

Warum Vary: Origin?

Wenn der Server Access-Control-Allow-Origin dynamisch setzt, informiert Vary: Origin Caches darüber, dass die Antwort vom anfragenden Origin abhängt. [1]

Wie lässt sich Preflight-Traffic reduzieren?

Preflight-Ergebnisse können mit Access-Control-Max-Age gecacht werden. Alternativ können Requests, wenn fachlich und sicherheitstechnisch sinnvoll, als einfache Requests gestaltet werden. [1][10][11]

Quellen und Referenzen

  1. MDN, Cross-Origin Resource Sharing (CORS)123456789101112131415
  2. WHATWG, Fetch Standard, CORS protocol123456789101112
  3. MDN, Same-origin policy123456
  4. MDN, Origin12
  5. MDN, Preflight request1234567
  6. MDN, Using the Fetch API12345678
  7. MDN, RequestInit12
  8. MDN, Access-Control-Allow-Origin123456
  9. MDN, Access-Control-Allow-Credentials123
  10. MDN, Access-Control-Max-Age1234
  11. MDN, CORS errors1234567
  12. MDN, CORS header Access-Control-Allow-Origin missing123
  13. MDN, Set-Cookie123
  14. MDN, Practical CORS configuration12
  15. MDN, Cross-site request forgery (CSRF)12
  16. OWASP, HTML5 Security Cheat Sheet, Cross Origin Resource Sharing123456789
  17. OWASP, HTTP Headers Cheat Sheet, Access-Control-Allow-Origin1234

War das hilfreich?

Neue Artikel per E-Mail erhalten

Eine kurze E-Mail pro neuem Wissens-Artikel. Kein Spam, Abmeldung mit einem Klick.

Wir nutzen Ihre E-Mail nur, um neue Artikel zu versenden. Keine Weitergabe an Dritte.

Zurück zu Wissen