
Die Meldung sieht ungefähr so aus, und die wichtige Hälfte ist ihr Ende:
Access to fetch at 'https://api.example.com/orders' from origin
'https://app.example.com' has been blocked by CORS policy: No
'Access-Control-Allow-Origin' header is present on the requested resource.
Lesen Sie sie als Satz darüber, wer sich geweigert hat. Der Server hat geantwortet. Ihre Anfrage hat den Browser verlassen, die andere Maschine erreicht und ist mit einer Antwort zurückgekommen. Der Browser hat es dann abgelehnt, diese Antwort an Ihr JavaScript zu übergeben, weil die Antwort nicht sagte, dass Ihre Origin sie lesen darf.
Diese eine Tatsache schließt das meiste aus, was man zuerst versucht. Im Netzwerk ist nichts kaputt. Der Server ist nicht ausgefallen. Es steht keine Firewall im Weg. Die Antwort liegt im Browser, und der Browser gibt sie nicht weiter.
Warum der Browser das überhaupt tut
Ohne das könnte jede Seite, die Sie besuchen, mit den Cookies, die ohnehin schon in Ihrem Browser liegen, unbemerkt Anfragen an Ihre Bank, Ihr Webmail oder die internen Werkzeuge Ihrer Firma stellen und die Antworten lesen.
Die Regel lautet also: JavaScript darf eine Anfrage an eine fremde Origin schicken, darf die Antwort aber nur dann lesen, wenn der antwortende Server sagt, dass diese Origin das darf. Die Erlaubnis muss von dem Server kommen, der gefragt wird, denn er ist der Einzige, der weiß, wer das lesen soll.
Zwei Folgen, die überraschen:
- Es gilt nur für JavaScript im Browser. Dieselbe Anfrage aus curl, aus Postman oder aus Ihrem eigenen Backend funktioniert, denn keines davon ist ein Browser, der jemandes Sitzung schützt. Eine Anfrage, die in Postman klappt und in der Seite scheitert, ist kein Beleg für einen kaputten Server.
- Es im Browser abzuschalten behebt nichts. Ein Flag oder eine Erweiterung, die die Prüfung ausschaltet, lässt den Fehler auf einer Maschine verschwinden, während ihn jeder Besucher weiter bekommt. Das ist ein Weg, die Diagnose zu bestätigen, keine Lösung, und einen Browser im Alltag so zu betreiben ist wirklich eine schlechte Idee.
Ablesen, welche Regel gescheitert ist
Das Ende der Meldung benennt die Regel, und es gibt drei, die Ihnen begegnen werden.
No ‘Access-Control-Allow-Origin’ header is present. (Es ist kein ‘Access-Control-Allow-Origin’-Header vorhanden.) Der einfache Fall. Der Server hat nichts über Erlaubnisse geschickt, also nimmt der Browser an, dass es keine gibt.
Response to preflight request doesn’t pass access control check. (Die Antwort auf die
Preflight-Anfrage besteht die Zugriffskontrolle nicht.) Der Fehlschlag ist passiert, bevor Ihre
Anfrage überhaupt abgeschickt wurde. Alles, was über eine einfache Anfrage hinausgeht, ein PUT
oder DELETE, ein eigener Header wie Authorization, ein JSON-Content-Type, lässt den Browser
zuerst eine OPTIONS-Anfrage schicken, die fragt, ob die eigentliche erlaubt ist. Wenn dieses
OPTIONS ein 404, ein 500, eine Weiterleitung oder ein 200 ohne die richtigen Header zurückgibt,
kommt die eigentliche Anfrage nie zustande.
*The value of ‘Access-Control-Allow-Origin’ must not be the wildcard ‘’ when credentials mode is
‘include’.** (Der Wert darf nicht der Platzhalter ‘*’ sein, wenn Anmeldeinformationen mitgeschickt
werden.) Sie schicken Cookies mit, und * ist dafür nicht genau genug. Der Server muss Ihre Origin
exakt benennen und Access-Control-Allow-Credentials: true hinzufügen.
Wohin die Lösung gehört
Auf den Server, dem die Ressource gehört, in jedem Fall. Nicht in Ihre Seite, nicht in den Browser und nicht in einen Proxy, den Sie vor Ihr eigenes Frontend setzen.
Wenn es Ihre API ist, heißt das, Access-Control-Allow-Origin mit der Origin zurückzugeben, die Sie
erlauben wollen, und für Preflights OPTIONS mit den erlaubten Methoden und Headern und einem
2xx-Status zu beantworten. Wenn es die API eines anderen ist und sie keinen Zugriff aus dem Browser
erlaubt, dann steht der Zugriff aus dem Browser nicht zur Verfügung: Rufen Sie sie aus Ihrem eigenen
Backend auf und lassen Sie Ihre Seite mit diesem sprechen.
Session Replay
Kostenlose Chrome-Erweiterung. Ein Klick auf der Seite, die sich falsch verhält, erfasst den Screenshot, die Konsole und das Netzwerkprotokoll und gibt Ihnen einen Link, den Sie ins Ticket einfügen können.
Auf localhost, wo die meisten es kennenlernen
http://localhost:3000 und http://localhost:8080 sind verschiedene Origins, also ist ein Frontend
auf dem einen Port, das eine API auf dem anderen aufruft, eine Cross-Origin-Anfrage und bekommt die
volle Behandlung.
Die richtige Antwort in der Entwicklung ist ein Proxy: Ihr Dev-Server leitet /api an die API
weiter, der Browser sieht also eine einzige Origin und die Frage stellt sich gar nicht. Jedes
moderne Frontend-Werkzeug hat das eingebaut, und es kostet eine Zeile Konfiguration. Die falsche
Antwort ist, Chrome mit abgeschalteter Web-Sicherheit zu starten, denn dann funktioniert der Code
nur für Leute, die das auch getan haben.
Was Ihr Code sieht
Nichts Brauchbares, und genau das macht die Fehlersuche hier so verwirrend.
Eine blockierte Antwort kommt nicht als Fehler an, den Sie untersuchen könnten. fetch() wird mit
dem generischen
TypeError: Failed to fetch
abgelehnt, ohne Status und ohne Body, denn die Seite lesen zu lassen, warum sie blockiert wurde,
würde genau die Information preisgeben, zu deren Schutz die Regel da ist.
Die CORS-Meldung in der Konsole ist also kein Fehler, den Ihr Code abgefangen hat. Es ist der Browser, der Ihnen, dem Entwickler, sagt, was er getan hat. Ihr Code kann sie nicht sehen, nicht protokollieren und nicht melden.
Das ist gut zu wissen, wenn die Meldung von jemand anderem kommt. Ein Nutzer, der in einen
CORS-Fehlschlag läuft, sieht eine Funktion, die stillschweigend nichts tut, Ihr Error-Tracking
verzeichnet ein Failed to fetch ohne Details, und der Satz, der es erklärt hätte, wurde in eine
Konsole geschrieben, die niemand aufbewahrt hat.
Wie Sie es in einem Bericht finden
Zwei Stellen, und Sie wollen beide.
Die Konsole trägt die CORS-Meldung mit der Origin, der URL und der gescheiterten Regel. Das
Netzwerkpanel zeigt die Anfrage selbst, und bei einem Preflight-Fehlschlag die
OPTIONS-Anfrage, die über der eigentlichen steht, mit ihrem eigenen Status. Öffnen Sie deren
Response-Header: was dort fehlt, ist die ganze Diagnose.
Wenn die Anfrage im Netzwerkpanel gar nicht auftaucht, hat der Browser sich schon vor dem Senden geweigert, und das heißt gemischte Inhalte oder eine Erweiterung, nicht CORS.
Die Kurzfassung
- Der Server hat geantwortet; der Browser hat Ihrem Code verwehrt, die Antwort zu lesen.
- Es gilt nur für JavaScript im Browser, also beweisen curl und Postman nichts.
- Das Ende der Meldung benennt die gescheiterte Regel. Lesen Sie das, nicht den Anfang.
- Preflight-Fehlschläge passieren vor Ihrer Anfrage, auf einem
OPTIONS, das der Browser für Sie geschickt hat. - Die Lösung gehört auf den antwortenden Server. Die Prüfung lokal abzuschalten ist eine Diagnose, keine Lösung.
- Nutzen Sie auf localhost einen Dev-Proxy, damit es nur eine Origin gibt.