Zum Hauptinhalt springen

Was ist CORS?

CORS ist die Browser-Antwort auf die Frage "Darf diese fremde Webseite meine API ansprechen?" — und einer der häufigsten Stolpersteine bei modernen Web-Apps.

Same-Origin-Policy

Standardmäßig dürfen Browser nur Anfragen an die gleiche Origin (Schema, Host, Port) machen, von der die Seite stammt. Eine Webseite auf https://app.example.com darf also nicht einfach https://api.beispiel.de via fetch() aufrufen — das wäre cross-origin. Diese Same-Origin-Policy ist der wichtigste Schutz vor unerlaubten API-Calls aus fremden Seiten (z. B. Phishing-Seiten, die im Hintergrund deine Banking-API ansprechen).

Wozu dient CORS?

CORS ist der gezielte Aufweichungsmechanismus: Du als API-Betreiber sagst dem Browser "Ja, diese fremde Webseite darf mich ansprechen — aber nur diese." Der Browser fragt also vorher beim Server nach, ob die Anfrage erlaubt ist, und führt sie nur aus, wenn der Server zustimmt. CORS schützt also nicht deine API — die kann jeder mit curl ansprechen — sondern es schützt deine Nutzer davor, dass eine fremde Webseite in ihrem Namen API-Calls macht.

Einfache Anfragen

Bei einer einfachen GET- oder POST-Anfrage (mit Standard-Content-Types) macht der Browser den Request einfach und prüft den Antwort-Header Access-Control-Allow-Origin:

Access-Control-Allow-Origin: https://app.example.com

Stimmt die Origin der aufrufenden Seite überein, darf das JavaScript die Antwort lesen. Sonst blockiert der Browser den Zugriff (die Anfrage selbst wurde aber bereits ausgeführt — wichtig für GET-Requests, die Side-Effects haben sollen, niemals!).

Preflight-Requests

Bei "nicht-einfachen" Anfragen (PUT, DELETE, Custom Headers, JSON-POSTs etc.) macht der Browser zuerst einen OPTIONS-Request zum Server (Preflight) und fragt: "Darf ich folgende Anfrage machen?". Erst wenn der Server zustimmt, kommt der eigentliche Request.

Antwort des Servers auf den Preflight:

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400

Max-Age sagt dem Browser, wie lange er die Preflight-Antwort cachen darf — sinnvoll auf 24h setzen, damit nicht jeder Request einen extra OPTIONS-Roundtrip kostet.

Cookies und Auth

Standardmäßig schickt der Browser bei Cross-Origin-Requests keine Cookies mit. Soll er das tun (z. B. weil deine API Session-Cookies nutzt), müssen drei Dinge stimmen:

  1. Im Frontend: fetch(url, { credentials: 'include' }).
  2. Server-Antwort: Access-Control-Allow-Credentials: true.
  3. Server darf nicht Access-Control-Allow-Origin: * verwenden — die Origin muss explizit angegeben werden.

Häufige Fehler

  • Allow-Origin: * auf einer API mit Auth: gefährlich, weil jede Webseite zugreifen darf. Allow-Origin sollte einer Whitelist entstammen.
  • Origin spiegeln: Access-Control-Allow-Origin: ${request.origin} ohne Validierung — erlaubt jedem Aufrufer Zugriff. Validiere gegen eine echte Whitelist.
  • OPTIONS nicht beantwortet: Wenn dein Server OPTIONS ignoriert oder mit 405 antwortet, schlägt der Preflight fehl und der eigentliche Request läuft nie.
  • CORS für CSRF-Schutz halten: CORS schützt nicht vor CSRF — ein Angreifer kann mit einem Formular trotzdem POST-Requests mit Cookies absetzen. Du brauchst CSRF-Tokens.

Konfigurationsbeispiel (Flask)

from flask_cors import CORS

CORS(app, resources={
    r"/api/*": {
        "origins": ["https://app.example.com"],
        "methods": ["GET", "POST", "PUT", "DELETE"],
        "allow_headers": ["Content-Type", "Authorization"],
        "supports_credentials": True,
        "max_age": 86400,
    }
})

Selbst prüfen: HTTP-Header

HSTS, CSP, X-Frame-Options & Co. Gib eine Domain ein und sieh in Sekunden, wie es um deinen CORS steht.

Auch im Lexikon