CORS, SOP, origin, nagłówki HTTP i błędy z nimi związane

Anna Buller

Anna Buller

07-09-2022

(aktualizacja: 10-09-2026)

No Access-Control-Allow-Origin header present – i już się w nas gotuje! Czym jest ten denerwujący CORS, dlaczego sprawia takie problemy i „utrudnia” pracę z API? A może to nie on utrudnia, lecz SOP? Robi się ciekawie. Dowiedz się więcej o komunikacji między originami i nie daj się pokonać błędom!

CORS, SOP, origin, nagłówki HTTP i błędy z nimi związane
CORS (Cross-Origin Resource Sharing) to mechanizm oparty na nagłówkach HTTP, który pozwala przeglądarce bezpiecznie komunikować się z serwerem o innym originie — mimo że domyślnie blokuje to SOP (Same-Origin Policy). Najczęstszy błąd, „No 'Access-Control-Allow-Origin' header is present”, oznacza, że serwer nie wysłał nagłówka zezwalającego na taki kontakt. W nauce można to obejść tymczasowym proxy, ale docelowo trzeba poprawnie skonfigurować nagłówki Access-Control-* po stronie backendu.

Czym jest SOP (Same-Origin Policy)?

Czym w uproszczeniu jest SOP?

Polskie tłumaczenie same-origin policy może brzmieć: reguła tego samego pochodzenia. W uproszczeniu SOP to mechanizm bezpieczeństwa zmniejszający ryzyko ataków, który reguluje komunikację między dwiema stronami internetowymi. Do takiej komunikacji może dojść tylko wtedy, kiedy strony mają ten sam origin, czyli (znów w uproszczeniu) zbieżny adres. Przykładowo strona http://www.devmentor.pl/assets/images skomunikuje się ze stroną http://www.devmentor.pl/subpage/blog.html.

Ale z powodu SOP komunikacja z poniższym adresem zostanie zablokowana (brakuje „jedynie" www): http://devmentor.pl/subpage/blog.html

Na Wikipedii znajdziesz więcej przykładów możliwych różnic między originami.

Czym jest origin i jak brzmi definicja SOP?

To teraz koniec uproszczeń. Oto definicja SOP z MDN:

Same-origin policy (reguła tego samego pochodzenia) to istotny mechanizm bezpieczeństwa, który określa sposób, w jaki dokument lub skrypt jednego pochodzenia (origin) może komunikować się z zasobem innego pochodzenia. Pozwala to na odizolowanie potencjalnie szkodliwych dokumentów i tym samym redukowane są czynniki sprzyjające atakom.

Origin to trzy nierozłączne elementy:

  • Protokół (schemat) – np. http i https
  • Host – np. devmentor.pl czy subdomena.devmentor.pl (to dwa różne hosty)
  • Port – jeśli nie jest określony, to jego domyślną wartością jest 80.

Jeżeli którykolwiek z powyższych elementów w originach obu komunikujących się źródeł jest inny, komunikacja zakończy się niepowodzeniem. W tym miejscu być może pomyślałeś, że to dziwne, ponieważ zdarzało Ci się już łączyć z API, które przecież mieściło się pod zupełnie innym adresem niż np. Twój http://localhost:5500/. I słusznie – tego typu komunikacja rzeczywiście jest umożliwiana. W przeciwnym razie duża część aplikacji internetowych w ogóle by nie działała. Więc jak to w końcu jest? Z pomocą przychodzi nam CORS, z którego dobrodziejstw nieświadomie korzystamy na co dzień.

Czym jest CORS (Cross-Origin Resource Sharing)?

Chciałbym zwrócić Twoją uwagę na fragment rozwinięcia skrótu: cross-origin. Od razu wskazuje on na to, że komunikacja (resource sharing) będzie zachodzić między skryptami/dokumentami o różnych originach. CORS to mechanizm bazujący na nagłówkach HTTP regulujący zasady tej komunikacji — inaczej mówiąc, polityka CORS to zestaw reguł, które serwer przesyła przeglądarce w nagłówkach odpowiedzi.

Właśnie w nagłówkach przeglądarka przesyła do odpytywanego serwera informacje, które pozwolą mu „zdecydować", czy ufa on stronie odpytującej (choćby naszemu http://localhost:5500/). Serwer także przesyła informacje do przeglądarki, m.in. z „prośbą" o zezwolenie na kontakt z jego originem. W nagłówkach przy tzw. prostym zapytaniu (ang. simple request) znajdzie się więc metoda HTTP (jedna z trzech: GET, HEAD lub POST) oraz – oprócz nagłówków automatycznie ustawianych przez przeglądarkę – tzw. CORS-safelisted request-header. Jeśli korzystałeś już z API, to kojarzysz zapewne np. nagłówek Content-Type.

Abyśmy mogli dostać się do zasobów danego serwera, ten, kto wystawia API, musi zadbać o odpowiednie ustawienia („pozwolenia") – nagłówki z serii Access-Control-…-…. Po stronie back endu ten fragment kodu mógłby wyglądać np. tak:

async function handler(req, res) {
  res.setHeader("Access-Control-Request-Method", "GET")
  res.setHeader("Access-Control-Allow-Origin", "https://devmentor.pl")
  res.setHeader("Access-Control-Allow-Headers", "Content-Type")
  // [...]
}

Oczywiście szczegóły dotyczące wymaganych nagłówków powinniśmy znaleźć w dokumentacji. Jeżeli chcesz obejrzeć wideo na temat działania CORS i nagłówków, to zapraszam Cię na kanał Artura Chmary do nagrania CORS w pigułce: działanie i naprawa. Chciałbym tylko uczulić Cię na jeden mały błąd: w filmie CORS przedstawiono jako „blokadę" komunikacji cross-origin, gdy w rzeczywistości CORS tę komunikację umożliwia (to SOP ją blokuje). Łatwo o taką pomyłkę z tej racji, że CORS często jest powodem błędów – mamy więc wrażenie, że utrudnia nam komunikację z API ;)

Czym różni się CORS od SOP?

CechaSOP (Same-Origin Policy)CORS (Cross-Origin Resource Sharing)
Co robiDomyślnie blokuje komunikację między różnymi originamiOtwiera tę komunikację tam, gdzie serwer na to pozwoli
Gdzie działaWbudowany mechanizm przeglądarki, zawsze aktywnyZestaw nagłówków HTTP, które musi ustawić serwer
Kto go konfigurujeNikt — to domyślne zachowanie przeglądarkiBackend/administrator API (nagłówki Access-Control-*)
Efekt braku konfiguracjiBlokada — tak działa z założeniaBłąd w konsoli, np. „No 'Access-Control-Allow-Origin' header…"

Jak podkreśla Mateusz, mentor Full Stack w devmentor.pl, na mentoringu ten sam błąd — mylenie SOP z CORS — pojawia się u 90% kursantów przy pierwszym kontakcie z zewnętrznym API. Najszybciej rozwiewa go prosta analogia: SOP to domyślnie zamknięte drzwi budynku („nikogo nie wpuszczamy, chyba że jesteś stąd"), a CORS to lista gości, którą właściciel budynku — czyli serwer — sam ustala i przekazuje ochronie, czyli przeglądarce. To serwer decyduje, kogo mimo wszystko wpuścić, a nie przeglądarka.

Jakie nagłówki HTTP odpowiadają za CORS?

To właśnie rodzina nagłówków Access-Control-* decyduje, czy i na jakich warunkach przeglądarka wpuści odpowiedź z innego originu. Serwer wysyła je w odpowiedzi (czasem też w odpowiedzi na wspomniany wcześniej preflight request), a przeglądarka je odczytuje i egzekwuje.

NagłówekDo czego służyPrzykładowa wartość
Access-Control-Allow-OriginOkreśla, które originy mogą odczytać odpowiedź serwerahttps://devmentor.pl lub * (dowolny origin)
Access-Control-Allow-MethodsLista metod HTTP dozwolonych w zapytaniach cross-originGET, POST, PUT
Access-Control-Allow-HeadersLista nagłówków żądania, które serwer akceptujeContent-Type, Authorization
Access-Control-Allow-CredentialsCzy przeglądarka może wysyłać cookies i dane uwierzytelniającetrue
Access-Control-Max-AgeJak długo (w sekundach) przeglądarka może cache'ować wynik preflight, by nie powtarzać go przy każdym zapytaniu86400

Wartość * w Access-Control-Allow-Origin jest wygodna przy publicznych, niewymagających uwierzytelnienia API, ale nie zadziała razem z Access-Control-Allow-Credentials: true — przeglądarka wymaga wtedy konkretnego originu, a nie „gwiazdki". To jeden z częstszych powodów, dla których nagłówki wydają się skonfigurowane poprawnie, a błąd CORS i tak się pojawia.

Z doświadczenia mentorów devmentor.pl wynika, że błędy związane z nagłówkami Access-Control-* to jeden z dziesięciu najczęściej zgłaszanych problemów na sesjach mentoringowych dotyczących pracy z API — wyprzedza je jedynie problem z limitem zapytań, opisany w artykule Limity API w nauce programowania – obejdź to!.

Jakie błędy związane z CORS możesz napotkać?

Być może właśnie z ich powodu tutaj jesteś. Skąd biorą się błędy związane z CORS? Podczas nauki programowania możesz napotkać co najmniej dwie sytuacje:

  1. Próbujesz skontaktować się ze starszym API, które nie ma zdefiniowanego CORS-a.
  2. Podczas używania JSON Servera schemat (protokół), z którego korzystasz, różni się od schematu adresu, pod którym mieszczą się pożądane zasoby.

Dlaczego pojawia się błąd „No Access-Control-Allow-Origin header present"?

Gdy próbujemy skontaktować się z API, które nie ma zdefiniowanego CORS-a (czyli np. dawno nie było aktualizowane), to napotkamy błąd: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Na czym polega ten błąd? Na poziomie przeglądarki i serwera, z którym próbujemy się skomunikować, dochodzi do wymiany informacji:

  1. Zanim przeglądarka wyśle do serwera zapytanie o zasoby, najpierw wykonuje tzw. preflight request – zapytanie o wytyczne, jakie przeglądarka powinna spełnić, by móc następnie wykonać bezpieczne zapytanie o zasoby.
  2. Serwer wysyła wytyczne, m.in. origin, na kontakt z którym przeglądarka powinna zezwolić (przeglądarka oczekuje, że otrzyma tę informację). Jest to nasz Access-Control-Allow-Origin header, którego brak powoduje błąd.

Czy to oznacza, że API bez CORS jest zupełnie bezużyteczne? Nie, jesteśmy w stanie obejść to za pomocą tzw. pośrednika (proxy). Ale uwaga: to rozwiązanie nie nadaje się na produkcję, służy jedynie nauce, testom i tworzeniu demo projektów np. do portfolio. W „prawdziwym" projekcie po prostu nie będziemy korzystać z takiego API, które nie spełnia wymogów bezpieczeństwa.

Jak działa proxy (pośrednik) przy błędach CORS?

Najpierw przyjrzyjmy się poniższemu URL: https://cors-anywhere.herokuapp.com/http://api.weatherapi.com/v1/future.json

Jego pierwsza część, to pośrednik (proxy): https://cors-anywhere.herokuapp.com/, a druga to nasze zapytanie do API pogodowego: http://api.weatherapi.com/v1/future.json

Jak to działa? Za chwilę powiem o tym, jak można stworzyć własne proxy, ale najpierw dowiedzmy się, jaki mechanizm tym rządzi.

  1. Proxy poprzedza nasze zapytanie do API.
  2. Otrzymuje odpowiedź z API (w tym miejscu bez proxy zobaczylibyśmy błąd No 'Access-Control-Allow-Origin' header…).
  3. Proxy do odpowiedzi z API dodaje nagłówek Access-Control-Allow-Origin.
  4. Przeglądarka nie zgłasza błędu z CORS, ponieważ otrzymuje nagłówek, którego oczekiwała, wysyła więc już do API właściwe zapytanie o zasoby.

Mateusz, mentor Full Stack w devmentor.pl, zauważa, że proxy typu cors-anywhere bywa zgłaszane jako „gotowe rozwiązanie" na rozmowach rekrutacyjnych — a to jeden z sygnałów, po których rekruter lub mentor techniczny od razu wie, że kandydat nie rozumie, dlaczego to backend, a nie przeglądarka, powinien odpowiadać za nagłówki CORS.

Jak stworzyć własne proxy z cors-anywhere?

Własne proxy możemy stworzyć dzięki umieszczeniu kodu dodającego odpowiednie nagłówki i opcje na darmowym serwisie do hostowania stron internetowych, np. Heroku. Skąd wziąć taki kod? Gotowe rozwiązanie znajdziesz na GitHubie: cors-anywhere. Jego instalację i deploy na Heroku w kilku prostych krokach przedstawia odpowiedź na Stack Overflow. Przed stworzeniem własnego proxy możemy przetestować działanie pośrednika dzięki demo cors-anywhere. Jako demo ma ono limity odpytań, lecz wystarczy do zapoznania się z tematem.

Dlaczego pojawia się błąd „Cross origin requests are only supported for protocol schemes"?

Pełna treść błędu może brzmieć: XMLHttpRequest cannot load localhost:4000/data.json. Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https, chrome-extension-resource.

Teoretycznie przy korzystaniu z lokalnego serwera, gdzie mamy różnicę portów (np. JSON Server otwiera się na localhost:3000, a webpack DevServer na localhost:8080), powinniśmy napotkać problem z CORS. Zwykle tak się jednak nie dzieje, ponieważ narzędzia pokroju JSON Servera zezwalają na komunikację z localhostem (po odpytaniu API w konsoli w przeglądarce w zakładce Network znajdziesz informacje o nagłówkach, w tym także nagłówku Access-Control-Allow-Origin).

Powyższy błąd wystąpi jednak, gdy uruchomisz pliki przez protokół file:// – czyli gdy np. otworzysz w przeglądarce plik bezpośrednio z katalogu. Wówczas adres będzie wyglądał podobnie do tego: file:///C:/Users/mateusz/Desktop/my-website/index.html

Treść błędu w tym przypadku może brzmieć również mniej więcej tak: Origin null is not allowed by Access-Control-Allow-Origin.

Lokalny system plików nie posiada originu (protokołu, hosta, portu), dlatego jest blokowany przez SOP. Swego czasu Firefox umożliwiał komunikację między tymi samymi katalogami i podkatalogami, lecz ze względów bezpieczeństwa zostało to wycofane.

Jak sobie z tym poradzić? Wystarczy użyć np. rozszerzenia do VS Code: Live Server. Pozwoli on otworzyć nasze pliki przez protokół http://. Adres w pasku przeglądarki zmieni się na podobny do tego: http://127.0.0.1:5500/index.html. Błąd nie powinien już występować.

Gdzie znaleźć pełną listę błędów CORS?

Na stronie MDN znajdziesz listę błędów związanych z CORS wraz z krótkimi wskazówkami co do ich przyczyny. Być może pomogą Ci one w rozwiązaniu problemu lub przynajmniej w sformułowaniu odpowiedniego zapytania w Google.

Błędy związane z CORS potrafią być frustrujące – wystarczy rzucić okiem na fora czy zakładki Issues popularnych narzędzi na GitHubie. Zapewne więc przyjdzie Ci nie raz się z nimi mierzyć. Dlatego już na początku warto zrozumieć temat komunikacji między przeglądarką a serwerem, znać nagłówki, które za nią odpowiadają, a na potrzeby nauki programowania wiedzieć o istnieniu proxy.

Konsultacja i warsztaty devmentor.pl

Masz pytania?