Menu

CORS in Express: CORS-Fehler beheben (mit und ohne cors)

Ein CORS-Fehler heißt, dass der Browser einer Seite das Lesen der Antwort deiner API verweigert hat, weil die Antwort den Origin der Seite nicht erlaubt hat. Du behebst ihn auf dem Server, indem du Access-Control-Allow-Origin sendest und Preflight-OPTIONS-Anfragen beantwortest, von Hand oder mit dem Paket cors.

Diese Seite enthält ausführbare Editoren. Bearbeiten, ausführen und Ausgabe sofort sehen.

Ein CORS-Fehler ("No 'Access-Control-Allow-Origin' header is present") heißt, dass eine Webseite auf einem Origin, etwa http://localhost:5173, deine API auf einem anderen Origin aufgerufen hat und der Browser der Seite das Lesen der Antwort verweigert hat, weil die Antwort diesen Origin nicht erlaubt hat. Die Lösung liegt auf dem Server: Sende Access-Control-Allow-Origin mit dem Origin der Seite und beantworte die Preflight-OPTIONS-Anfrage des Browsers mit den Methoden und Headern, die du akzeptierst. In Express sind das ein paar Zeilen Middleware oder app.use(cors()) aus dem Paket cors.

Die Antwort ist die Produktliste, und ihr Headers-Tab zeigt access-control-allow-origin: https://app.example.com: Eine Seite auf diesem Origin darf sie lesen. Sende jetzt OPTIONS /api/products/1 mit den Headern Origin: https://app.example.com und Access-Control-Request-Method: PUT. Das ist der Preflight, den ein Browser vor einem PUT schickt, und die Antwort ist 204 mit den erlaubten Methoden und Headern und ohne Body.

Was der Browser prüft

Seiten mit demselben Origin brauchen nichts. Bei einer Cross-Origin-Anfrage liest der Browser diese Response-Header:

HeaderBedeutung
Access-Control-Allow-OriginDer eine Origin, der die Antwort lesen darf, oder * für alle
Access-Control-Allow-MethodsMethoden, die in der eigentlichen Anfrage erlaubt sind (nur Preflight)
Access-Control-Allow-HeadersRequest-Header, die die Seite senden darf, etwa Authorization (nur Preflight)
Access-Control-Allow-Credentialstrue, wenn die Seite Cookies senden und die Antwort lesen darf
Access-Control-Max-AgeSekunden, die der Browser die Preflight-Antwort cachen darf
Vary: OriginSagt Caches, dass die Antwort vom Request-Header Origin abhängt

Ein einfacher GET oder ein POST wie aus einem Formular geht direkt an den Server, und der Browser prüft Access-Control-Allow-Origin in der Antwort. Der Server führt die Anfrage trotzdem aus; CORS entscheidet nur, ob die Seite das Ergebnis sieht. Vor einer Anfrage mit JSON-Body, einem Authorization-Header oder einer Methode wie PUT oder DELETE kommt eine Preflight-OPTIONS-Anfrage, und die eigentliche Anfrage wird nur gesendet, wenn die Preflight-Antwort sie erlaubt. Deshalb muss eine CORS-Middleware OPTIONS beantworten, bevor irgendeine Route oder Auth-Prüfung läuft.

Mehrere Origins erlauben, mit Credentials

Access-Control-Allow-Origin enthält einen Origin oder *, nie eine Liste. Um mehrere zu erlauben, vergleich den Origin-Header der Anfrage mit einer Allow-List und schick ihn zurück, wenn er passt:

Die erste Anfrage hat keinen Origin-Header, wie ein Aufruf aus curl oder von einer Seite mit demselben Origin, deshalb bekommt sie {"origin":null} und keine CORS-Header. Sende GET /api/me mit dem Header Origin: http://localhost:5173, und der Headers-Tab zeigt diesen Origin zurückgespiegelt plus access-control-allow-credentials: true. Ändere den Header in Origin: https://evil.example, und beide CORS-Header verschwinden: Der Server antwortet weiterhin, aber ein Browser würde der Seite das Lesen verweigern. Access-Control-Allow-Credentials: true erlaubt der Seite, Cookies zu senden (mit fetch(url, { credentials: 'include' })), und Browser lehnen es zusammen mit * ab, Credentials brauchen also immer den genauen Origin.

Das Paket cors

Das Paket cors macht dieselbe Arbeit über Optionen. Installier es mit npm install cors:

const cors = require('cors');

// Any origin, no cookies: fine for a public read-only API.
app.use(cors());

// Only your frontend, with cookies.
app.use(cors({
    origin: ['https://app.example.com', 'http://localhost:5173'],
    credentials: true,
    methods: ['GET', 'POST', 'PUT', 'DELETE'],
}));

// One route only.
app.get('/api/public', cors(), handler);

Ohne Optionen sendet cors() Access-Control-Allow-Origin: * und beantwortet jeden Preflight. Nimm * nur für öffentliche Daten, für die kein Login nötig ist.

Häufige Fehler

CORS nach den Routen oder nach einer Auth-Middleware einbinden. Die Preflight-OPTIONS-Anfrage hat keinen Authorization-Header, eine vorher platzierte Auth-Prüfung beantwortet sie also mit 401, und der Browser meldet einen CORS-Fehler. Registrier die CORS-Middleware zuerst.

* zusammen mit Credentials setzen. Browser lehnen die Antwort ab. Spiegle stattdessen den konkreten Origin zurück.

CORS im Frontend beheben wollen. Request-Optionen wie mode: 'no-cors' heben die Einschränkung nicht auf; sie liefern eine undurchsichtige Antwort, die du nicht lesen kannst. Nur die Header des Servers lösen das Problem.

Wie app.use() und die Reihenfolge von Middleware funktionieren, steht unter Express Middleware.

Häufig gestellte Fragen

Warum funktioniert meine API in Postman oder curl, aber nicht im Browser?

CORS wird nur von Browsern durchgesetzt. Postman, curl und andere Server ignorieren die CORS-Header und lesen die Antwort so oder so. Der Browser gibt die Antwort nicht an Skripte einer Seite mit anderem Origin weiter, solange der Server diesen Origin nicht erlaubt.

Ist CORS ein Sicherheitsfeature für meinen Server?

Es schützt die Nutzer, nicht deinen Server. Es verhindert, dass eine bösartige Seite Antworten deiner API mit den Cookies des Nutzers liest. Jeder kann deine API weiterhin aus einem Skript oder von einem Server aufrufen, Authentifizierung und Autorisierung bleiben also deine Aufgabe.

Was zählt als anderer Origin?

Ein Origin besteht aus Schema, Host und Port zusammen. http://localhost:5173 und http://localhost:3000 sind verschiedene Origins, weil sich die Ports unterscheiden, und https://example.com und https://api.example.com unterscheiden sich im Host.

Kann ich CORS in der Entwicklung umgehen?

Ja, indem du die Anfrage same-origin machst: Konfiguriere den Dev-Server deines Frontends (zum Beispiel server.proxy in Vite) so, dass er /api an den Express-Server weiterleitet. Der Browser spricht dann nur mit einem Origin, und es braucht keine CORS-Header.

Illustration der Programmiersprachen bei Coddy

Lerne mit Coddy zu programmieren

Los geht's