Wer kennt es nicht? Man arbeitet an einem neuen Webprojekt und plötzlich erscheint die rote Fehlermeldung in der Browser-Konsole: Access to fetch at '...' has been blocked by CORS policy. CORS-Fehler gehören zu den häufigsten Stolpersteinen in der modernen Webentwicklung. Doch statt frustriert zu raten, hilft es, den dahinterliegenden Mechanismus einmal grundlegend zu verstehen. Dieser Guide erklärt Ihnen CORS von Grund auf, damit Sie Fehler künftig gezielt lösen können.

Was ist die Same-Origin Policy (SOP)?

Bevor wir über CORS sprechen, müssen wir die Sicherheitsbarriere des Browsers verstehen: die Same-Origin Policy (SOP). Sie besagt, dass ein Skript einer Website standardmäßig nur auf Ressourcen zugreifen darf, die vom exakt selben Ursprung (Origin) stammen. Ein Origin wird durch drei Komponenten definiert: Protokoll, Hostname und Port.

Vergleich von Origins
https://example.com/page1  →  https://example.com/page2     ✅ Gleicher Origin
https://example.com        →  http://example.com             ❌ Anderes Protokoll (HTTPS vs. HTTP)
https://example.com        →  https://api.example.com        ❌ Anderer Hostname (Subdomain)
https://example.com        →  https://example.com:8080       ❌ Anderer Port

Ohne die Same-Origin-Policy könnte eine Schadseite im Hintergrund mittels JavaScript vertrauliche Daten von Ihrem Online-Banking oder Ihrem E-Mail-Konto abfragen, sofern Sie dort noch eingeloggt sind. Die SOP schützt Nutzer somit vor massivem Missbrauch.

Was genau ist CORS?

Manchmal müssen moderne Webanwendungen jedoch bewusst mit APIs auf anderen Domains interagieren – zum Beispiel, wenn Ihre Frontend-App auf myapp.com läuft, die Daten-API jedoch unter api.myapp.com erreichbar ist. Hier kommt CORS (Cross-Origin Resource Sharing) ins Spiel.

CORS ist ein standardisierter Mechanismus, der die strikte Same-Origin Policy gezielt lockert. Er erlaubt es einem Server, dem Browser via HTTP-Antwort-Header explizit mitzuteilen, welche externen Ursprünge auf seine Daten zugreifen dürfen.

Wie funktioniert CORS im Detail?

1. Einfache Anfragen (Simple Requests)

Bei klassischen GET- oder POST-Anfragen mit Standard-Content-Types (z. B. application/x-www-form-urlencoded) sendet der Browser die Anfrage direkt ab. Er fügt automatisch den Origin-Header hinzu. Der Server antwortet und liefert die CORS-Header mit, die der Browser dann überprüft:

Einfache CORS-Anfrage
// 1. Browser sendet Anfrage:
GET /api/data HTTP/1.1
Host: api.example.com
Origin: https://myapp.com

// 2. Server antwortet mit Freigabe-Header:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://myapp.com
Content-Type: application/json

{"data": "Erfolgreich geladen!"}

2. Preflight-Anfragen (OPTIONS)

Wenn eine Anfrage "komplexer" ist – etwa weil sie JSON-Daten (application/json) sendet, spezielle HTTP-Methoden wie PUT oder DELETE nutzt oder benutzerdefinierte Header mitsendet –, geht der Browser auf Nummer sicher. Er schickt vorab eine Test-Anfrage (einen sogenannten Preflight-Request mittels OPTIONS) an den Server.

Preflight-Anfrage (OPTIONS)
// 1. Browser fragt um Erlaubnis (Preflight):
OPTIONS /api/users HTTP/1.1
Host: api.example.com
Origin: https://myapp.com
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: Content-Type, Authorization

// 2. Server erlaubt die Aktion:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400

// 3. Erst jetzt sendet der Browser die eigentliche DELETE-Anfrage

Die wichtigsten CORS-Antwort-Header

Essenzielle CORS-Header
Access-Control-Allow-Origin: https://myapp.com   // Bestimmt, wer zugreifen darf
Access-Control-Allow-Methods: GET, POST, PUT       // Erlaubte HTTP-Methoden
Access-Control-Allow-Headers: Content-Type, Auth   // Erlaubte Header in der Anfrage
Access-Control-Allow-Credentials: true             // Erlaubt das Senden von Cookies/Session-IDs
Access-Control-Max-Age: 86400                      // Cache-Dauer für Preflight in Sekunden
Access-Control-Expose-Headers: X-Request-Id        // Macht Header für JS lesbar

Praktische Server-Konfigurationen

Express.js (Node.js)
import cors from 'cors';

app.use(cors({
  origin: ['https://myapp.com', 'https://staging.myapp.com'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
  maxAge: 86400
}));
Nginx
location /api/ {
    add_header Access-Control-Allow-Origin "https://myapp.com" always;
    add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE" always;
    add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;

    if ($request_method = OPTIONS) {
        return 204;
    }
}
PHP
header("Access-Control-Allow-Origin: https://myapp.com");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE");
header("Access-Control-Allow-Headers: Content-Type, Authorization");

if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(204);
    exit;
}

Typische CORS-Fehler und wie man sie vermeidet

  • Nutzung von Wildcard * mit Credentials: Wenn Sie Access-Control-Allow-Credentials: true nutzen, blockiert der Browser die Anfrage, falls der Origin auf * steht. Sie müssen stattdessen die konkrete Domain im Header zurückliefern.
  • CORS ist kein echter API-Schutz: CORS schützt ausschließlich den Endnutzer im Browser. Server-zu-Server-Anfragen (z. B. via Postman, curl oder Backend-Code) umgehen CORS-Restriktionen mühelos. Sichern Sie sensible Endpunkte immer zusätzlich per Authentifizierung ab.
  • Fehlendes always bei Nginx: Ohne das Schlüsselwort always in der Nginx-Konfiguration liefert der Webserver die CORS-Header bei Fehlern (wie 404 oder 500) nicht aus, was die Fehlersuche erschwert.
  • Fehlgeschlagene OPTIONS-Requests: Wenn Ihre API-Routen bei einem OPTIONS-Aufruf mit einem Statuscode wie 401, 404 oder 405 antworten, scheitert der gesamte nachfolgende API-Call. Sorgen Sie dafür, dass OPTIONS-Preflights immer sofort mit 200 oder 204 zurückgegeben werden.

Entdecken Sie unsere kostenlosen Web-Tools

Konvertieren Sie URLs, dekodieren Sie Base64-Strings oder formatieren Sie unübersichtlichen JSON-Code im Handumdrehen.