CORS cross origin – jak działa i jak poprawnie skonfigurować dostęp między domenami
Mechanizm CORS cross origin decyduje o tym, czy przeglądarka pozwoli skryptowi działającemu na jednej domenie odczytać odpowiedź serwera z innej domeny. Zasada CORS cross origin wywodzi się z Same-Origin Policy, czyli fundamentalnego zabezpieczenia, które blokuje odczyt danych między różnymi źródłami, jeśli serwer wyraźnie na to nie zezwoli. Origin składa się z trzech elementów: schematu, hosta i portu, a zmiana choćby jednego z nich sprawia, że żądanie staje się cross-origin. Programiści najczęściej spotykają ten problem podczas łączenia frontendu na localhost:3000 z API na localhost:8000, przy integracji sklepu z zewnętrznym feedem produktowym albo przy pobieraniu czcionek z CDN. Komunikat o zablokowanym żądaniu nie oznacza błędu serwera, bo serwer zwykle odpowiedział poprawnie, ale przeglądarka odmówiła udostępnienia treści kodowi JavaScript wobec braku odpowiednich nagłówków. Zrozumienie tego rozróżnienia skraca diagnostykę z kilku godzin do kilku minut i pozwala uniknąć rozwiązań, które otwierają API na cały świat.
Same-Origin Policy jako fundament kontroli źródeł
Origin to trójka złożona ze schematu, hosta i portu. Adresy https://sklep.pl oraz http://sklep.pl mają różne schematy, a https://api.sklep.pl i https://sklep.pl różne hosty, więc przeglądarka traktuje je jako osobne źródła. Nawet port 8080 zamiast standardowego 443 wystarczy, aby żądanie zostało uznane za cross-origin i objęte pełną kontrolą.
Same-Origin Policy powstała po to, aby złośliwy skrypt na przypadkowej stronie nie odczytał salda z otwartej sesji bankowości internetowej. Przeglądarka wysyła żądanie, serwer je przetwarza, ale odpowiedź trafia do kodu JavaScript dopiero wtedy, gdy nagłówki potwierdzą zgodę drugiej strony na współdzielenie danych z konkretnym źródłem.
Żądania proste kontra żądania preflight
Żądanie proste to GET, HEAD lub POST z ograniczonym zestawem nagłówków i typem treści text/plain, application/x-www-form-urlencoded albo multipart/form-data. Taki ruch trafia od razu do serwera, a przeglądarka jedynie weryfikuje odpowiedź. Dodanie nagłówka Authorization lub typu application/json natychmiast zmienia klasyfikację i uruchamia dodatkową procedurę kontrolną.
Wtedy startuje preflight: przeglądarka najpierw wysyła metodę OPTIONS z nagłówkami Access-Control-Request-Method oraz Access-Control-Request-Headers, pytając serwer o zgodę. Dopiero pozytywna odpowiedź uruchamia właściwe zapytanie z danymi. Każdy preflight to dodatkowa podróż w obie strony, kosztująca zwykle od 30 do 150 milisekund realnego czasu odpowiedzi.
Nagłówki HTTP, które przesądzają o dostępie
Konfiguracja CORS cross origin opiera się na kilku nagłówkach odpowiedzi. Najważniejszy pozostaje Access-Control-Allow-Origin, który wskazuje dozwolone źródło. Wartość gwiazdki otwiera zasób dla wszystkich, natomiast konkretna domena ogranicza dostęp do jednego frontendu. Serwer powinien odbijać wyłącznie adresy z własnej białej listy, nigdy dowolną wartość przysłaną przez klienta.
Access-Control-Allow-Methods wylicza dozwolone metody, a Access-Control-Allow-Headers nazwy nagłówków, które klient może przesłać. Jeśli aplikacja sklepu wysyła token w nagłówku X-Api-Key podczas pobierania stanów magazynowych dla modelu klawiatura gamingowa mechaniczna w cenie około 450 zł, brak tej nazwy na liście zablokuje żądanie już na etapie preflightu.
Access-Control-Max-Age buforuje wynik preflightu, zwykle na 600 lub 86400 sekund, co realnie odciąża backend. Access-Control-Expose-Headers udostępnia skryptowi nagłówki niestandardowe, na przykład X-Total-Count przy paginacji katalogu, w którym obok siebie stoją monitor do komputera za 1200 zł oraz klawiatura mechaniczna 60 procent szerokości za 350 zł.
| Nagłówek | Rola | Przykładowa wartość |
|---|---|---|
| Access-Control-Allow-Origin | Wskazuje dozwolone źródło | https://sklep.pl |
| Access-Control-Allow-Methods | Dozwolone metody HTTP | GET, POST, PUT, DELETE |
| Access-Control-Allow-Headers | Dozwolone nagłówki żądania | Content-Type, Authorization |
| Access-Control-Allow-Credentials | Przesyłanie ciasteczek i sesji | true |
| Access-Control-Max-Age | Czas cache dla preflightu | 86400 |
Konfiguracja serwera i popularnych platform
W Nginx wystarczy dyrektywa add_header wewnątrz bloku location, uzupełniona o osobną obsługę metody OPTIONS zwracającej status 204 bez treści. Częsty błąd polega na pominięciu parametru always, przez co nagłówki znikają przy odpowiedziach 4xx i 5xx, a diagnostyka staje się mylnie skomplikowana i czasochłonna.
Apache realizuje to samo przez moduł mod_headers i regułę Header set w pliku konfiguracyjnym lub .htaccess. W CMS-ach reguły potrafią kolidować z wtyczkami bezpieczeństwa, które chronią wrażliwe adresy, w tym podstronę WordPress logowanie oraz endpointy REST API. Zanim dopiszesz nagłówki, sprawdź, czy wtyczka nie nadpisuje ich później we własnym filtrze.
Na własnej maszynie, choćby na OVH VPS w wariancie za około 30 zł miesięcznie, warto trzymać politykę w jednym miejscu zamiast rozsypywać ją po kodzie aplikacji. W Node.js sprawdza się middleware cors z jawną listą origin, w Django biblioteka django-cors-headers, a w Spring Boot adnotacja konfiguracyjna na poziomie kontrolera.

Integracje z usługami chmurowymi mają własne panele akceptacji domen. Google Workspace cena zaczyna się od kilkudziesięciu złotych za użytkownika miesięcznie, a korzystanie z powiązanych API wymaga dopisania adresu witryny do listy autoryzowanych źródeł w konsoli chmurowej. Bez tego kroku żaden nagłówek po stronie serwera nie odblokuje żądania.
Najczęstsze błędy i szybka diagnostyka
Numer jeden to zwracanie gwiazdki razem z Access-Control-Allow-Credentials ustawionym na true. Specyfikacja zabrania takiej kombinacji, więc przeglądarka odrzuca odpowiedź mimo pozornie poprawnej konfiguracji. Rozwiązaniem jest dynamiczne zwracanie konkretnej domeny na podstawie zweryfikowanej listy dozwolonych źródeł po stronie backendu.
Drugi klasyk to podwójne nagłówki, gdy zarówno serwer WWW, jak i framework dopisują Access-Control-Allow-Origin. Przeglądarka widzi dwie wartości i blokuje odczyt. Trzeci przypadek dotyczy przekierowań: jeśli żądanie preflight kończy się kodem 301 lub 302, procedura kończy się niepowodzeniem, bo OPTIONS nie podąża za przekierowaniem.
Lista kontrolna przy debugowaniu
- Sprawdź zakładkę Sieć i porównaj nagłówki żądania OPTIONS z odpowiedzią serwera.
- Zweryfikuj, czy protokół, host i port zgadzają się co do znaku, łącznie z ukośnikiem końcowym.
- Wykonaj to samo zapytanie narzędziem curl, aby wykluczyć błąd po stronie backendu.
- Upewnij się, że metoda OPTIONS nie wymaga uwierzytelnienia i nie trafia w firewall aplikacyjny.
- Wyłącz tymczasowo cache CDN, który potrafi serwować starą odpowiedź bez nagłówków.
Odróżnienie błędu sieciowego od błędu polityki oszczędza mnóstwo czasu. Jeśli curl zwraca dane, a przeglądarka nie, problem leży wyłącznie w nagłówkach. Gdy karta produktu tablet graficzny Wacom ładuje ceny asynchronicznie z zewnętrznego API, brak jednej nazwy nagłówka na liście dozwolonych potrafi wygasić cały moduł.
Bezpieczeństwo, wydajność i pozycjonowanie strony
Otwarcie API gwiazdką bywa kuszące, ale przy endpointach zwracających dane użytkowników jest to prosta droga do wycieku. Zamiast tego stosuj białą listę domen, krótkie tokeny i rozdzielenie zasobów publicznych od prywatnych. Publiczny cennik może być otwarty, natomiast historia zamówień powinna wymagać ścisłej weryfikacji źródła.
Nadmiar preflightów wydłuża czas ładowania danych, co przekłada się na gorsze wyniki Interaction to Next Paint i słabsze pozycjonowanie strony w Google. Ustawienie Access-Control-Max-Age na 86400 sekund oraz przeniesienie API pod subdomenę obsługiwaną tym samym certyfikatem redukuje liczbę zbędnych zapytań kontrolnych o kilkadziesiąt procent.
Świadome projektowanie stron internetowych uwzględnia politykę źródeł już na etapie architektury, a nie po wdrożeniu. Sklep eksportujący feed do Google Merchant, ładujący opinie z zewnętrznego widgetu i renderujący listę modeli, w której króluje klawiatura mechaniczna, musi mieć spójne reguły dla wszystkich integracji, bo od tego zależy stabilność i pozycjonowanie strony.
Jak szybko naprawić błąd CORS cross origin na serwerze produkcyjnym?
Zacznij od odczytania dokładnej treści komunikatu w konsoli, bo przeglądarka zwykle wskazuje brakujący element. Następnie w zakładce Sieć znajdź żądanie OPTIONS i sprawdź, jaki status oraz jakie nagłówki zwrócił serwer. Jeśli status wynosi 401, 403 albo 404, oznacza to, że metoda OPTIONS nie jest obsługiwana i trzeba dodać dla niej osobną regułę zwracającą 204. Kolejny krok to uzupełnienie Access-Control-Allow-Headers o wszystkie nazwy wysyłane przez frontend, łącznie z Authorization i Content-Type. Na końcu zweryfikuj, czy nagłówki nie są dublowane przez serwer WWW i framework jednocześnie. Cała procedura zajmuje zwykle kilkanaście minut i nie wymaga zmian w kodzie frontendu.
Czy wyłączenie kontroli źródeł w przeglądarce jest bezpiecznym rozwiązaniem?
Nie, uruchamianie przeglądarki z wyłączonymi zabezpieczeniami rozwiązuje problem wyłącznie na jednej maszynie i pozostawia go u wszystkich użytkowników. Taka przeglądarka traci ochronę przed odczytem danych z dowolnej otwartej sesji, więc każda odwiedzona strona może sięgnąć po zawartość innych kart. W środowisku deweloperskim znacznie lepiej sprawdza się proxy w serwerze dewelperskim, które przekierowuje zapytania na ten sam origin, albo tunel lokalny z jedną domeną dla frontendu i backendu. Rozszerzenia dopisujące nagłówki także maskują problem, bo produkcyjna wersja aplikacji nadal będzie blokowana. Docelowym rozwiązaniem zawsze pozostaje poprawna konfiguracja po stronie serwera, kontrolowana przez zespół i wersjonowana razem z kodem.
Co oznacza nagłówek Access-Control-Allow-Credentials i kiedy go stosować?
Ten nagłówek zezwala przeglądarce na dołączanie ciasteczek, nagłówka Authorization i certyfikatów klienta do żądań między domenami, a następnie na udostępnienie odpowiedzi kodowi JavaScript. Używaj go tylko wtedy, gdy uwierzytelnienie faktycznie opiera się na sesji ciasteczkowej, na przykład w panelu administracyjnym rozdzielonym na osobną subdomenę. Warunkiem działania jest zwrócenie konkretnej domeny w Access-Control-Allow-Origin, ponieważ gwiazdka jest w tym trybie zabroniona. Po stronie klienta trzeba dodatkowo ustawić opcję credentials w fetch lub withCredentials w bibliotece HTTP. Pamiętaj też o atrybutach SameSite oraz Secure w ciasteczkach, bo bez nich nowoczesne przeglądarki i tak odrzucą sesję przy żądaniu z innej domeny.
