Co to jest CORS?

Autor: Opublikowano

W skrócie: CORS (Cross-Origin Resource Sharing) to mechanizm, dzięki któremu serwer może pozwolić przeglądarce na udostępnienie odpowiedzi skryptowi działającemu pod innym adresem pochodzenia. O zgodzie decydują nagłówki odpowiedzi serwera, a ich przestrzegania pilnuje przeglądarka. Błąd CORS zwykle oznacza więc problem z uprawnieniem do odczytu odpowiedzi, niekoniecznie brak połączenia z serwerem.

Dlaczego przeglądarka ogranicza dostęp między adresami

Przeglądarki stosują zasadę tego samego pochodzenia, czyli same-origin policy. Ogranicza ona możliwość odczytywania przez skrypt danych z innego pochodzenia. Dzięki temu strona otwarta w jednej karcie nie może swobodnie pobierać i odczytywać prywatnych danych z innych serwisów, w których użytkownik jest zalogowany.

Pochodzenie (origin) to zestaw trzech elementów: protokołu, nazwy hosta i portu. Zmiana choćby jednego z nich oznacza inne pochodzenie. Ścieżka po nazwie hosta nie ma tutaj znaczenia.

Dla strony https://sklep.example porównanie wygląda tak:

Adres docelowyTo samo pochodzenie?Powód
https://sklep.example/produktyTakZmienia się tylko ścieżka
https://api.sklep.exampleNieInna nazwa hosta
http://sklep.exampleNieInny protokół
https://sklep.example:8443NieInny port niż domyślny 443

CORS pozwala serwerowi kontrolowanie poluzować tę zasadę. Nie jest metodą HTTP, taką jak GET lub POST, lecz mechanizmem opartym na nagłówkach HTTP.

Prosty przykład: sklep pobiera listę produktów

Załóżmy, że strona działa pod adresem https://sklep.example, a API, czyli interfejs udostępniający dane aplikacji, pod https://api.sklep.example. Skrypt strony chce pobrać produkty:

fetch('https://api.sklep.example/produkty')
  .then(odpowiedz => odpowiedz.json())
  .then(produkty => console.log(produkty));

Ponieważ adresy mają różne pochodzenie, przeglądarka dodaje do żądania nagłówek wskazujący pochodzenie strony:

Origin: https://sklep.example

Serwer API może odpowiedzieć danymi i nagłówkiem ze zgodą:

Access-Control-Allow-Origin: https://sklep.example

Przeglądarka porównuje tę wartość z pochodzeniem strony. Jeśli zgoda pasuje, udostępnia odpowiedź skryptowi. Jeśli nagłówka brakuje albo wskazuje inny adres, skrypt nie może odczytać odpowiedzi — nawet gdy serwer zwrócił status 200 OK.

Nagłówek ze zgodą musi pochodzić z serwera API. Dodanie Access-Control-Allow-Origin w kodzie żądania po stronie przeglądarki nie rozwiązuje problemu.

Kiedy przeglądarka najpierw wysyła OPTIONS

Nie każde żądanie między różnymi pochodzeniami jest wysyłane od razu. Przy niektórych przeglądarka najpierw wykonuje preflight, czyli zapytanie wstępne sprawdzające zgodę serwera.

Przykładowo wysłanie zamówienia metodą POST z typem treści application/json wywołuje taki etap. Podobnie działa użycie nagłówka Authorization. Zapytanie wstępne może wyglądać następująco:

OPTIONS /zamowienia HTTP/1.1
Origin: https://sklep.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type

Serwer powinien odpowiedzieć zgodą obejmującą pochodzenie, metodę i potrzebne nagłówki:

Access-Control-Allow-Origin: https://sklep.example
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type

Dopiero po udanym sprawdzeniu przeglądarka wysyła właściwy POST. Odpowiedź na ten POST również musi zawierać odpowiedni nagłówek Access-Control-Allow-Origin.

Nie każde żądanie POST wymaga preflight. Znaczenie mają także nagłówki i typ przesyłanej treści; zwykły GET bez dodatkowych nagłówków często nie potrzebuje zapytania wstępnego.

Cookies i gwiazdka: dwie ważne pułapki

Publiczne API, którego odpowiedzi mają być dostępne dla skryptów z dowolnego pochodzenia, może zwracać:

Access-Control-Allow-Origin: *

Gwiazdka nie jest jednak właściwym rozwiązaniem dla żądań wysyłanych z cookies w trybie uwzględniającym dane uwierzytelniające. W takim przypadku kod może używać credentials: 'include', a serwer musi wskazać konkretne dozwolone pochodzenie oraz zwrócić:

Access-Control-Allow-Origin: https://sklep.example
Access-Control-Allow-Credentials: true

Sama konfiguracja CORS nie gwarantuje przesłania cookies. Obowiązują również reguły cookies, między innymi SameSite, oraz ograniczenia prywatności przeglądarki.

Jeśli serwer obsługuje kilka zaufanych pochodzeń, powinien sprawdzać nagłówek Origin względem listy dozwolonych wartości. Bezrefleksyjne odsyłanie dowolnego otrzymanego pochodzenia ze zgodą na dane uwierzytelniające może ujawnić prywatne odpowiedzi. Przy odpowiedzi zależnej od Origin należy również zadbać o Vary: Origin, aby pamięć podręczna rozróżniała warianty odpowiedzi.

Jak diagnozować błąd CORS

W narzędziach deweloperskich przeglądarki otwórz kartę sieciową i wykonaj cztery sprawdzenia:

  1. Porównaj protokół, host i port strony oraz API.
  2. Sprawdź, czy występuje żądanie OPTIONS i czy serwer poprawnie na nie odpowiada.
  3. Obejrzyj nagłówki odpowiedzi właściwego żądania, także przy błędzie serwera.
  4. Sprawdź, czy zgoda obejmuje używaną metodę, nagłówki i ewentualne dane uwierzytelniające.

To, że wywołanie działa w curl, nie dowodzi poprawności CORS: takie narzędzie nie egzekwuje ograniczeń przeglądarki. Ustawienie mode: 'no-cors' również nie odblokuje odczytu JSON — daje odpowiedź nieprzezroczystą, której treść nie jest dostępna dla skryptu.

CORS nie zastępuje logowania, kontroli uprawnień ani ochrony przed CSRF, czyli wymuszaniem niechcianych działań przez inną stronę. Część żądań może dotrzeć do serwera mimo zablokowania odczytu odpowiedzi. Zacznij naprawę od konfiguracji API lub kontrolowanego pośrednika, a nie od wyłączania zabezpieczeń przeglądarki.

Zobacz też: Co to jest API?

Przeczytaj także

Dowiedz się, jak PHP-FPM obsługuje żądania PHP, czym różni się od serwera WWW i jak dobrać ustawienia procesów do ruchu oraz zasobów hostingu.
Poznaj BIND, otwarte oprogramowanie serwera DNS: sprawdź, jak obsługuje domeny, czym różnią się jego role i kiedy warto samodzielnie go wdrożyć.
Poznaj najważniejsze Security Headers, ich rolę w ochronie strony oraz sposób sprawdzania i wdrażania nagłówków bez blokowania potrzebnych funkcji.