Krótka odpowiedź
CORS (Cross-Origin Resource Sharing) to mechanizm bezpieczeństwa przeglądarki, który blokuje żądania JavaScript (fetch(), XMLHttpRequest) do innej domeny, portu lub protokołu, jeśli serwer nie odpowie nagłówkiem Access-Control-Allow-Origin dopasowanym do originu strony1. Dla żądań z niestandardowymi nagłówkami, metodami PUT/DELETE albo Content-Type: application/json przeglądarka najpierw wysyła żądanie OPTIONS, czyli preflight1. Błąd naprawiasz na serwerze, nie w przeglądarce – dodając prawidłowe nagłówki CORS do odpowiedzi API.
Budujesz frontend, wysyłasz żądanie fetch() do API i przeglądarka wyrzuca czerwony błąd: „Access to fetch at 'https://api.example.com’ from origin 'https://mysite.com’ has been blocked by CORS policy: No 'Access-Control-Allow-Origin’ header is present”. To samo zapytanie działa w Postmanie, w cURL i w terminalu – ale nie w przeglądarce, bo CORS to reguła egzekwowana wyłącznie po stronie klienta.
Spis treści
ToggleCo to jest „origin” i „cross-origin”
Origin (pochodzenie) to kombinacja: protokół + domena + port (protokół to np. różnica między HTTP a HTTPS). Dwa URL-e mają ten sam origin, jeśli wszystkie trzy elementy się zgadzają1:
| URL A | URL B | Ten sam origin? | Dlaczego |
|---|---|---|---|
https://example.com/a |
https://example.com/b |
TAK | Ten sam protokół, domena, port |
https://example.com |
http://example.com |
NIE | Inny protokół (https vs http) |
https://example.com |
https://api.example.com |
NIE | Inna subdomena |
https://example.com |
https://example.com:8080 |
NIE | Inny port |
https://mysite.com |
https://api.example.com |
NIE | Inna domena |
Cross-origin = żądanie z jednego originu do innego. Przykład: Twój frontend na https://mysite.com wysyła fetch() do API na https://api.example.com – to jest cross-origin request i podlega CORS.
Dlaczego CORS istnieje – problem bezpieczeństwa
Bez CORS przeglądarka pozwalałaby dowolnej stronie wysyłać żądania do dowolnego serwera z cookies i tokenami użytkownika. Wyobraź sobie:
- Odwiedzasz złośliwą stronę
evil.com - JavaScript na
evil.comwysyła fetch() dobank.com/api/transfer?amount=10000&to=hacker - Przeglądarka automatycznie dołącza Twoje cookies (bo jesteś zalogowany na bank.com)
- Bank przetwarza przelew – bo widzi prawidłowe cookies
CORS temu zapobiega: przeglądarka blokuje cross-origin request, chyba że serwer (bank.com) wyraźnie powie „pozwalam na żądania z evil.com” – co oczywiście nie zrobi.
Ważne: CORS to mechanizm przeglądarki, nie serwera. Serwer odpowiada normalnie na każde żądanie (cURL, Postman, backend → backend). To przeglądarka sprawdza nagłówki CORS i blokuje odpowiedź, jeśli serwer nie pozwala1.
Jak CORS działa – mechanizm krok po kroku
Proste żądania (Simple Requests)
„Proste” żądanie nie wywołuje preflightu. Musi spełniać jednocześnie: metodę GET, HEAD lub POST, tylko bezpieczne nagłówki (Accept, Accept-Language, Content-Language, Content-Type) i – jeśli jest Content-Type – wartość ograniczoną do application/x-www-form-urlencoded, multipart/form-data lub text/plain1. Dla takiego żądania przeglądarka:
- Wysyła żądanie do serwera z nagłówkiem
Origin: https://mysite.com - Serwer odpowiada z nagłówkiem
Access-Control-Allow-Origin: https://mysite.com(lub*= każdy origin) - Przeglądarka sprawdza: czy
Access-Control-Allow-Originpasuje do mojego originu? - Jeśli TAK → odpowiedź przekazana do JavaScript
- Jeśli NIE → odpowiedź zablokowana, JavaScript dostaje błąd CORS
Preflight request (OPTIONS)
Dla żądań spoza powyższych warunków (PUT, DELETE, custom nagłówki jak Authorization, Content-Type: application/json) przeglądarka najpierw wysyła żądanie OPTIONS (preflight)1:
- Przeglądarka: OPTIONS /api/data → „Hej serwer, czy mogę wysłać PUT z nagłówkiem Authorization z originu https://mysite.com?”
- Serwer: 204 No Content + nagłówki:
Access-Control-Allow-Origin: https://mysite.comAccess-Control-Allow-Methods: GET, POST, PUT, DELETEAccess-Control-Allow-Headers: Authorization, Content-Type
- Przeglądarka: „OK, serwer pozwala” → wysyła właściwe żądanie PUT
Preflight to „pytanie o pozwolenie” – jeśli serwer nie odpowie prawidłowo, właściwe żądanie nie jest wysyłane w ogóle.
Jak naprawić błąd CORS – konfiguracja serwera
Nagłówek Access-Control-Allow-Origin
Serwer musi zwracać nagłówek Access-Control-Allow-Origin z odpowiednią wartością:
# Pozwól na żądania z konkretnego originu:
Access-Control-Allow-Origin: https://mysite.com
# Pozwól na żądania z KAŻDEGO originu (mniej bezpieczne):
Access-Control-Allow-Origin: *
Uwaga: * (wildcard) nie działa z credentials: 'include' (cookies/auth). Jeśli Twój frontend wysyła cookies, musisz podać konkretny origin.
Ryzyko: Access-Control-Allow-Origin: * nigdy nie łączy się z Access-Control-Allow-Credentials: true – przeglądarka odrzuci taką kombinację, więc technicznie nie da się „przypadkiem” wysłać cookies przy wildcardzie2. Prawdziwe ryzyko jest odwrotne: część zespołów, próbując to obejść, dynamicznie odbija każdy nadchodzący Origin jako dozwolony (zamiast sprawdzać go z listą), co w praktyce daje efekt wildcardu z włączonymi credentials i otwiera API na dowolną domenę.
Apache (.htaccess)
# Pozwól na wszystkie originy:
Header set Access-Control-Allow-Origin "*"
# Pozwól na konkretny origin:
Header set Access-Control-Allow-Origin "https://mysite.com"
# Pełna konfiguracja (z preflight):
Header set Access-Control-Allow-Origin "https://mysite.com"
Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
Header set Access-Control-Allow-Headers "Authorization, Content-Type"
# Obsłuż preflight OPTIONS:
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=204,L]
Nginx
location /api/ {
add_header Access-Control-Allow-Origin "https://mysite.com" always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
if ($request_method = OPTIONS) {
return 204;
}
}
Node.js (Express)
const cors = require('cors');
// Pozwól na wszystkie originy:
app.use(cors());
// Lub konkretny origin:
app.use(cors({
origin: 'https://mysite.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Authorization', 'Content-Type'],
credentials: true // jeśli wysyłasz cookies
}));
WordPress (functions.php)
add_action('init', function() {
header('Access-Control-Allow-Origin: https://mysite.com');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Authorization, Content-Type');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(204);
exit;
}
});
Lub użyj wtyczki WP CORS (wordpress.org) – konfiguracja z poziomu wp-admin.
Najczęstsze scenariusze i rozwiązania
Frontend React/Next.js → WordPress REST API
Frontend na https://app.example.com pobiera wpisy z REST API WordPress na https://blog.example.com/wp-json/wp/v2/posts. Rozwiązanie: dodaj nagłówek CORS w WordPress (functions.php lub .htaccess jak wyżej).
Frontend → zewnętrzne API (np. OpenAI, Stripe)
NIE wysyłaj żądań do zewnętrznych API bezpośrednio z frontendu – nawet jeśli naprawisz CORS, ujawnisz klucz API w kodzie JavaScript (widoczny dla każdego). Rozwiązanie: stwórz proxy na swoim serwerze (API Route w Next.js, endpoint w Express) – frontend wysyła do Twojego backendu, backend wysyła do zewnętrznego API z kluczem.
Localhost podczas developmentu
Frontend na http://localhost:3000 → API na http://localhost:8080. Różne porty = różne originy = CORS. Rozwiązania:
- Proxy w devserver: Next.js:
next.config.js→rewrites. Vite:vite.config.js→server.proxy. Frontend wysyła do siebie, devserver proxy’uje na API. - CORS na backendzie (dev only):
Access-Control-Allow-Origin: http://localhost:3000. - Rozszerzenie przeglądarki: „Allow CORS” (Chrome) – wyłącza CORS lokalnie. Tylko do developmentu!
Ważne nagłówki CORS – pełna lista
| Nagłówek | Co robi | Przykład |
|---|---|---|
Access-Control-Allow-Origin |
Dozwolony origin | https://mysite.com lub * |
Access-Control-Allow-Methods |
Dozwolone metody HTTP | GET, POST, PUT, DELETE |
Access-Control-Allow-Headers |
Dozwolone nagłówki żądania | Authorization, Content-Type |
Access-Control-Allow-Credentials |
Czy pozwolić na cookies | true |
Access-Control-Max-Age |
Jak długo cache preflight (sekundy) | 86400 (24h) |
Access-Control-Expose-Headers |
Które nagłówki odpowiedzi JS widzi | X-Total-Count, Link |
Nagłówki i ich działanie sprawdzone w dokumentacji MDN oraz w specyfikacji Fetch (WHATWG)13.
Najczęściej zadawane pytania
Dlaczego Postman/cURL nie mają problemu z CORS?
CORS to mechanizm przeglądarki, nie serwera ani protokołu HTTP jako takiego. Postman, cURL i połączenia backend-to-backend nie są przeglądarkami i nie implementują polityki CORS, więc to samo żądanie przechodzi bez przeszkód poza przeglądarką, a w JavaScript w przeglądarce zostaje zablokowane.
Czy Access-Control-Allow-Origin: * jest bezpieczne?
Dla publicznych API bez logowania (dane pogodowe, kursy walut, publiczne listy) – tak. Dla API z autentykacją (cookies, tokeny) nie da się tego bezpiecznie połączyć z credentials – przeglądarka odrzuca kombinację wildcardu z Access-Control-Allow-Credentials: true. Dla endpointów wymagających logowania serwer musi zwracać konkretny, dozwolony origin, nie gwiazdkę.
Jak pozwolić na wiele originów?
Nagłówek Access-Control-Allow-Origin akceptuje tylko jedną wartość, nie listę. Żeby obsłużyć wiele originów, serwer sprawdza nagłówek Origin przychodzącego żądania, porównuje go z listą dozwolonych domen i dynamicznie zwraca w odpowiedzi dokładnie ten origin, jeśli znajduje się na liście.
CORS a CDN?
CDN (Cloudflare, Bunny) cache’uje odpowiedzi razem z nagłówkami CORS. Jeśli CDN zapisze w cache’u odpowiedź z Access-Control-Allow-Origin ustawionym dla jednej domeny, inna domena może dostać tę samą, nieprawidłową dla niej odpowiedź z cache’u. Rozwiązaniem jest nagłówek Vary: Origin na serwerze – mówi CDN „trzymaj osobną wersję odpowiedzi w cache’u dla każdego originu”.
Źródła
Zweryfikowano: wrzesień 2026Definicje, przykłady nagłówków i zasady preflightu sprawdziliśmy w dokumentacji MDN i specyfikacji Fetch.
- Cross-Origin Resource Sharing (CORS)DokumentacjaMDN Web Docsdeveloper.mozilla.org
- Reason: CORS header 'Access-Control-Allow-Origin’ does not match / credentialsDokumentacjaMDN Web Docsdeveloper.mozilla.org
- Fetch Standard – HTTP CORS protocol, CORS-safelisted methodsStandardWHATWGfetch.spec.whatwg.org






