CORS wyjaśniony: preflight, nagłówki i naprawianie błędów CORS
CORS wyjaśniony
CORS wyjaśniony: preflight, nagłówki, błędy i poprawki | We.Inc
Jak działa CORS: proste żądania i preflight, wszystkie nagłówki Access-Control, zasady dotyczące credentials, typowe błędy w konsoli i poprawka po stronie serwera dla Express, Next.js i nginx.
CORS (cross-origin resource sharing) to sposób, w jaki serwer informuje przeglądarkę, że JavaScript z innego originu może odczytywać jego odpowiedzi. Serwer odpowiada nagłówkiem Access-Control-Allow-Origin i nagłówkami pokrewnymi. Żądania z innymi metodami, własnymi nagłówkami lub treścią JSON najpierw wysyłają preflight OPTIONS. Poprawka zawsze należy do serwera, który wywołujesz, nigdy do strony.
Nagłówki odpowiedzi CORS w skrócie
Nagłówek
Na co pozwala
Access-Control-Allow-Origin
Który origin może odczytać odpowiedź (jeden origin lub *)
Access-Control-Allow-Methods
Metody dozwolone po preflighcie
Access-Control-Allow-Headers
Nagłówki żądania dozwolone po preflighcie
Access-Control-Allow-Credentials
Ciasteczka i dane uwierzytelniające wysyłane z żądaniem (wartość musi wynosić true)
Access-Control-Expose-Headers
Nagłówki odpowiedzi, które skrypt może odczytać
Access-Control-Max-Age
Liczba sekund, przez które odpowiedź na preflight może być buforowana
Błąd CORS wygląda tak, jakby Twój kod był zepsuty, a tymczasem żądanie zwykle bez problemu dotarło do serwera. Przeglądarka po prostu odmówiła przekazania odpowiedzi Twojemu skryptowi. Poniższy symulator pozwala opisać żądanie i konfigurację serwera, a potem pokazuje, czy zostanie wysłany preflight, czy przeglądarka przepuści odpowiedź i jaki komunikat zobaczysz w konsoli.
Ustaw origin strony, metodę, ewentualny własny nagłówek i to, czy wysyłane są ciasteczka.
Ustaw, co odpowiada serwer: dozwolony origin, metody, nagłówki i flagę credentials.
Przeczytaj werdykt: czy potrzebny jest preflight, czy przechodzi i jaki powód podaje konsola, jeśli się nie powiedzie.
Skopiuj pasujący fragment kodu serwera poniżej i dodaj go do API, nie do frontendu.
Gdy w grę wchodzą ciasteczka, zwracaj dokładny origin żądający z listy dozwolonych, bo symbol wieloznaczny jest odrzucany przy credentials.
Dodawaj Vary: Origin zawsze, gdy dozwolony origin zmienia się w zależności od żądania, aby cache nie podawał odpowiedzi jednej strony innej stronie.
Odpowiadaj na OPTIONS statusem 2xx i bez przekierowania. Preflight, który trafia na przekierowanie do logowania, za każdym razem kończy się niepowodzeniem.
Wyłączenie CORS we własnej przeglądarce tylko ukrywa problem na Twoim komputerze. Odwiedzający nadal widzą błąd.
Wywołania między serwerami nigdy nie dotyczą CORS. Jeśli nie kontrolujesz API, wywołuj je ze swojego backendu.
Najczęściej zadawane pytania
Dlaczego Postman lub curl działa, a przeglądarka nie?
CORS jest egzekwowany tylko przez przeglądarki, aby skrypty jednej strony nie mogły odczytywać danych innej strony z użyciem Twoich ciasteczek. Narzędzia takie jak curl nie uruchamiają niezaufanego kodu strony, więc go ignorują. Odpowiedź serwera była identyczna; to tylko przeglądarka zdecydowała się jej nie przekazać.
Co wywołuje żądanie preflight?
Każda metoda inna niż GET, HEAD lub POST, każdy nagłówek spoza kilku bezpiecznych (Accept, Accept-Language, Content-Language, Content-Type, Range) albo Content-Type inny niż kodowanie formularza, multipart lub text/plain. Wysyłanie application/json to najczęstsza przyczyna.
Czy mogę naprawić CORS po stronie frontendu?
Nie. Ustawienie nagłówków Access-Control w Twoim żądaniu nic nie daje, a mode: no-cors zwraca nieprzezroczystą odpowiedź, której Twój skrypt nie może odczytać. Albo API doda nagłówki, albo Twój własny serwer pobierze dane i je przekaże.
Czy mogę użyć Access-Control-Allow-Origin: * razem z ciasteczkami?
Nie. Gdy żądanie zawiera credentials, przeglądarka odrzuca wieloznaczny origin i wymaga dokładnego originu oraz Access-Control-Allow-Credentials: true. To samo dotyczy symboli wieloznacznych na listach metod, nagłówków i nagłówków udostępnianych.
Dlaczego mój skrypt nie może odczytać własnego nagłówka odpowiedzi?
Skrypt z innego originu domyślnie widzi tylko krótką listę bezpiecznych nagłówków odpowiedzi. Wymień pozostałe w Access-Control-Expose-Headers, na przykład X-Request-Id, a staną się dostępne do odczytu.
Ten sam origin i dlaczego przeglądarka blokuje odczyt
Origin to schemat, host i port razem. https://app.example.com i https://api.example.com to różne originy, podobnie jak wersje http i https tej samej domeny. Domyślnie skrypt może wysłać żądanie do innego originu, ale nie może odczytać tego, co wróci.
CORS to zgoda na wyjątek. API wymienia originy, którym ufa, a przeglądarka egzekwuje tę listę w imieniu odwiedzającego. Dlatego nagłówek musi pochodzić z serwera, który wywołujesz.
Proste żądania i preflighty
Zwykłe GET albo POST ze zwykłego formularza wychodzi od razu z nagłówkiem Origin. Następnie przeglądarka sprawdza w odpowiedzi dozwolony origin, zanim przekaże ją Twojemu kodowi.
Wszystko poza tym najpierw wysyła żądanie OPTIONS z nagłówkami Access-Control-Request-Method i Access-Control-Request-Headers. Właściwe żądanie wychodzi dopiero wtedy, gdy serwer zatwierdzi oba. Access-Control-Max-Age pozwala przeglądarce zapamiętać tę zgodę, choć przeglądarki ograniczają, jak długo.
Jak czytać błąd w konsoli
Ze względów bezpieczeństwa Twój JavaScript dowiaduje się tylko, że fetch się nie powiódł, z błędem TypeError i bez szczegółów. Prawdziwa przyczyna pojawia się w konsoli przeglądarki i wskazuje nagłówek, którego brakowało lub który był błędny. Porównaj ją z tabelą komunikatów poniżej.
Typowe błędy CORS i jak je naprawić
Treść komunikatów różni się w zależności od przeglądarki. Poniżej są przyczyny w brzmieniu, jakie nadaje im Firefox na MDN; Chrome mówi to samo dłuższymi zdaniami.
Przyczyny CORS w konsoli i poprawka po stronie serwera
Przyczyna w konsoli
Co to oznacza
Poprawka na serwerze
CORS header 'Access-Control-Allow-Origin' missing
Odpowiedź w ogóle nie zawierała nagłówka allow-origin
Dodaj nagłówek do każdej odpowiedzi, także do błędów
CORS header 'Access-Control-Allow-Origin' does not match
Nagłówek wskazuje inny origin
Odeślij origin wywołującego z listy dozwolonych
Credential is not supported if the CORS header 'Access-Control-Allow-Origin' is '*'
Wysłano ciasteczka, ale serwer odpowiedział *
Zwróć dokładny origin i Allow-Credentials: true
expected 'true' in CORS header 'Access-Control-Allow-Credentials'
Wysłano credentials, ale brakuje flagi
Wyślij Access-Control-Allow-Credentials: true
Did not find method in CORS header 'Access-Control-Allow-Methods'
Preflight nie zatwierdził metody
Wymień PUT, PATCH lub DELETE w Allow-Methods
missing token in CORS header 'Access-Control-Allow-Headers' from CORS preflight channel
Nagłówek żądania, taki jak Authorization, nie został zatwierdzony
Wymień go w Allow-Headers
CORS preflight channel did not succeed
OPTIONS nie powiodło się, przekierowało lub zwróciło błąd
Odpowiadaj na OPTIONS statusem 204 z nagłówkami
Multiple CORS header 'Access-Control-Allow-Origin' not allowed
Dwie warstwy (aplikacja i proxy) dodały nagłówek
Ustawiaj go tylko w jednym miejscu
Fragmenty kodu serwera
Express: lista dozwolonych z credentials
Odsyła origin tylko wtedy, gdy jest na liście, odpowiada na preflighty statusem 204 i dodaje Vary, aby cache rozdzielał originy.
Route handler w Next.js
Publiczny endpoint bez ciasteczek może używać symbolu wieloznacznego. Wyeksportuj OPTIONS, aby preflight dostał własną odpowiedź.
nginx przed API
Flaga always zachowuje nagłówek także w odpowiedziach z błędem, więc 500 nadal pokazuje swój prawdziwy status zamiast błędu CORS.
Strona przeglądarki: wysyłanie ciasteczek między originami
To credentials: "include" włącza na serwerze surowsze zasady dotyczące credentials.
Symulator żądań CORS
Opisz żądanie i nagłówki CORS serwera, aby zobaczyć, czy zostanie wysłany preflight i czy przeglądarka pozwoli Twojemu skryptowi odczytać odpowiedź.
Żądanie
Odpowiedź serwera
Origin strony
Origin API
Metoda
Content-Type
Brak treści
Dodatkowy nagłówek żądania (puste, jeśli brak)
Wysyłaj ciasteczka (credentials: include)
Access-Control-Allow-Origin
Access-Control-Allow-Methods
Access-Control-Allow-Headers
Access-Control-Allow-Credentials: true
Werdykt
Ten sam origin: CORS nie ma zastosowania, odpowiedź można odczytać.
Bez preflightu: to proste żądanie.
Potrzebny preflight: przeglądarka najpierw wysyła OPTIONS.
Powód: metoda nie jest GET, HEAD ani POST.
Powód: nagłówek {v} nie jest na liście bezpiecznych.
Powód: Content-Type {v} nie jest żadnym z trzech prostych typów.