Skip to content
Powrót do bloga
Poradniki

Kody statusu HTTP: ściąga (1xx-5xx) z przykładami

Kompletny przewodnik po kodach HTTP od 1xx do 5xx z przykładami, częstymi błędami (401 vs 403, 301 vs 302) i wpływem na SEO. Zobacz ściągę online.

14 min czytania

Kody statusu HTTP: ściąga (1xx-5xx) z przykładami

Otwierasz DevTools, a zakładka Network jest w połowie czerwona. Endpoint zwraca 502 na produkcji, 200 lokalnie, a kolega na Slacku właśnie zapytał: „powinno być 401 czy 403?”. Kody statusu HTTP wyglądają prosto: trzy cyfry, pięć kategorii. Ale zły wybór ujawnia informacje, psuje SEO i zamienia dyżury on-call w koszmar.

Ten przewodnik to kompletna ściąga kodów statusu HTTP dla pracujących programistów. Dostajesz trzy rzeczy: (1) tabelę szybkiego dostępu z każdym kodem spotykanym w realnych systemach, (2) macierze decyzyjne dla par, które najczęściej się myli (301 vs 302, 401 vs 403, 404 vs 410, 502 vs 504), oraz (3) sekcję narzędziową pokazującą, jak sprawdzać kody statusu z curl, fetch i Python requests. Każdy kod opisany niżej opiera się na RFC 9110, aktualnym standardzie semantyki HTTP, oraz na IANA HTTP Status Code Registry.

Szybka referencja: wszystkie kody statusu HTTP

Oto kody, z którymi spotkasz się na produkcji, pogrupowane według klas. Dodaj tę tabelę do zakładek; reszta artykułu wyjaśnia te trudniejsze.

KodNazwaKiedy go zobaczysz
100ContinueWysyłanie dużego ciała POST z Expect: 100-continue
101Switching ProtocolsHandshake WebSocket, upgrade do HTTP/2
103Early HintsSerwer wysyła nagłówki Link przed właściwą odpowiedzią
200OKDomyślny sukces dla GET, PUT, PATCH
201CreatedPOST, który tworzy zasób (zwraca Location)
202AcceptedAsynchroniczne zadanie zakolejkowane, jeszcze niewykonane
204No ContentSukces DELETE, PUT bez ciała do zwrócenia
206Partial ContentRange request, przewijanie wideo, wznawialne pobieranie
301Moved PermanentlyStary URL wycofany, wyszukiwarki przenoszą link equity
302FoundTymczasowe przekierowanie, oryginalny URL nadal kanoniczny
303See OtherWzorzec Post/Redirect/Get po wysłaniu formularza POST
304Not ModifiedWarunkowe GET z pasującym ETag lub If-Modified-Since
307Temporary RedirectJak 302, ale metoda i ciało są zachowane
308Permanent RedirectJak 301, ale metoda i ciało są zachowane
400Bad RequestNiepoprawny JSON, brakujące wymagane pole, błąd schematu
401UnauthorizedBrak danych uwierzytelniających lub wygasły token
403ForbiddenUwierzytelniony, ale bez uprawnień
404Not FoundZasób nie istnieje (lub go ukrywasz)
405Method Not AllowedPOST do endpointu obsługującego tylko GET (musi zawierać Allow)
408Request TimeoutKlient zbyt długo wysyłał request
409ConflictAwaria optymistycznej blokady, duplikat klucza
410GoneZasób trwale usunięty, nie wróci
415Unsupported Media TypeZły Content-Type, np. XML do API JSON
422Unprocessable ContentSkładnia poprawna, semantyka niepoprawna (błąd walidacji)
425Too EarlyRyzyko replay TLS 1.3 early-data
428Precondition RequiredSerwer wymaga If-Match w celu zapobiegania utraconym aktualizacjom
429Too Many RequestsLimit żądań przekroczony (musi zawierać Retry-After)
451Unavailable for Legal ReasonsDMCA, RODO takedown, geo-block
500Internal Server ErrorNieobsłużony wyjątek w twoim kodzie
501Not ImplementedMetoda lub funkcja nieobsługiwana (rzadkie w REST)
502Bad GatewayUpstream zwrócił niepoprawną odpowiedź
503Service UnavailableTryb konserwacji lub przeciążenie
504Gateway TimeoutUpstream nie odpowiedział w odpowiednim czasie
507Insufficient StorageWebDAV wyczerpał miejsce na dysku
508Loop DetectedPętla nieskończonych przekierowań lub rekurencja w WebDAV
511Network Authentication RequiredCaptive portal w hotelowym lub lotniskowym WiFi

Reszta artykułu rozkłada każdą klasę na czynniki pierwsze: macierze decyzyjne, antywzorce i konsekwencje SEO wynikające z błędnego wyboru.

Jak działają kody statusu HTTP (anatomia trzycyfrowego kodu)

Dlaczego trzy cyfry?

Kody statusu HTTP są trzycyfrowe, ponieważ HTTP/0.9 potrzebowało sygnału o stałej szerokości: wystarczająco małego, by parser mógł szybko się rozgałęzić, i wystarczająco dużego, by zostawić miejsce na nowe kody. Trzy cyfry dają 900 możliwych wartości (100–999) i to aż nadto: rejestr IANA używa dziś tylko około 60 z nich.

Pierwsza cyfra to klasa. Druga i trzecia to konkretny kod wewnątrz klasy. Klient, który nie rozpoznaje 418, powinien obsłużyć go jak zwykły 4xx. RFC 9110 §15 mówi to wprost: klienci muszą traktować nierozpoznane kody jak x00 ich klasy.

Pięć kategorii — przegląd

KlasaZnaczenieWymagane ciało?Cache’owalne domyślnie?
1xxInformacyjne — przejściowe, więcej w drodzeNieNie
2xxSukces — request został zrozumiany i zaakceptowanyCzęstoZależy od metody
3xxPrzekierowanie — potrzebne dalsze działanieOpcjonalnie301, 308 tak; 302, 307 nie
4xxBłąd klienta — twoja wina, popraw requestTak (wyjaśnij)Zwykle nie
5xxBłąd serwera — nasza wina, retry może pomócTak (wyjaśnij)Nie

Kolumna „cache’owalne domyślnie” ma znaczenie. CDN-y i przeglądarki agresywnie i na zawsze cache’ują 301 i 308, więc wybór złego kodu przekierowania na produkcji jest trudny do cofnięcia, bo użytkownicy mają przekierowanie zapisane lokalnie. Wrócimy do tego w sekcji SEO.

Jeśli interesuje cię struktura URL (na której operują kody przekierowań), Kodowanie i dekodowanie URL prowadzi przez procent-encoding, query stringi i bajtowy pipeline, który decyduje o tym, co w ogóle czyni URL poprawnym.

1xx — informacyjne (kiedy faktycznie je zobaczysz)

Większość programistów spędza lata bez bezpośredniego widoku 1xx. To odpowiedzi przejściowe: serwer mówi klientowi „jestem tu, leć dalej”. Browser DevTools zwykle je ukrywa, a większość bibliotek HTTP zwija je do finalnej odpowiedzi.

Dla każdego kodu poniżej referencja statusów HTTP w MDN jest najprzyjaźniejszym źródłem porównawczym, jeśli potrzebujesz drugiego spojrzenia na definicję.

100 Continue

Klient wysyła Expect: 100-continue w nagłówkach i czeka przed transmisją dużego ciała żądania. Serwer odpowiada 100 Continue, jeśli jest gotów przyjąć ciało, lub 4xx, jeśli i tak request odrzuci. Oszczędza to pasmo przy dużych uploadach: nie ma sensu wysyłać 200 MB, jeśli serwer i tak odrzuci request z powodu brakującego nagłówka.

curl -v -H "Expect: 100-continue" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @big-file.bin \
  https://api.example.com/upload

Jeśli w verbose nie widzisz < HTTP/1.1 100 Continue, twój klient prawdopodobnie wyciął ten nagłówek albo serwer go nie obsługuje.

101 Switching Protocols

Handshake przekształcający połączenie HTTP w połączenie WebSocket lub HTTP/2. Klient wysyła Upgrade: websocket, serwer odpowiada 101 Switching Protocols i od tego momentu połączenie mówi innym protokołem. Zobaczysz to w zakładce Network każdej aplikacji czatu, dashboardu na żywo lub narzędzia do współpracy.

103 Early Hints

Stosunkowo nowy kod (RFC 8297, 2017), który pozwala serwerowi wysyłać nagłówki Link z preload hints przed gotowością głównej odpowiedzi. Przeglądarka zaczyna pobierać CSS i JS, podczas gdy serwer wciąż renderuje. Stan na 2026: Cloudflare, Fastly i Vercel obsługują 103 na produkcji; to nowoczesna alternatywa dla HTTP/2 server push (który został wycofany w Chrome).

HTTP/1.1 103 Early Hints
Link: </styles.css>; rel=preload; as=style
Link: </app.js>; rel=preload; as=script

HTTP/1.1 200 OK
Content-Type: text/html
...

Sprawdzenie antywzorca. Jeśli klient nigdy nie widzi kodów 1xx, kiedy się ich spodziewasz, problem zwykle leży w reverse proxy. Starsze wersje nginx wycinają Expect: 100-continue i 103 Early Hints. Sprawdź konfigurację proxy, zanim założysz, że serwer jest zepsuty.

2xx — sukces (poza samym 200)

Zwracanie 200 OK na wszystko to najczęstszy code-smell w API REST. Rodzina 2xx niesie informację semantyczną, która czyni klientów mądrzejszymi, a cache wydajniejszymi.

200 OK

Domyślny. GET zwraca zasób, PUT zwraca zaktualizowany zasób (lub 204), PATCH zwraca załatany zasób. Jeśli nie masz powodu, by użyć bardziej konkretnego kodu, użyj 200.

201 Created

POST, który tworzy nowy zasób, powinien zwracać 201 plus nagłówek Location wskazujący na nowy zasób. W ten sposób klienci RESTful odkrywają kanoniczny URL rzeczy, którą właśnie stworzyli.

HTTP/1.1 201 Created
Location: /api/users/42
Content-Type: application/json

{"id": 42, "name": "Ada Lovelace"}

202 Accepted

Serwer przyjął request, ale jeszcze nie zakończył przetwarzania. Użyj tego dla pracy asynchronicznej; klient powinien odpytywać, subskrybować webhook lub sprawdzać endpoint statusu. Sparuj z ID zadania w ciele odpowiedzi.

204 No Content

Sukces, brak ciała. Częste dla DELETE (zasób przepadł, co miałbyś zwrócić?) i dla operacji PUT, gdy klient już zna nowy stan. Przeglądarki nie zmienią bieżącej strony, jeśli wysłanie formularza zwróci 204; przydatne dla akcji typu fire-and-forget w aplikacjach SPA.

206 Partial Content

Zwracane dla range requestów: klient poprosił o bajty 1000-2000 nagłówkiem Range: bytes=1000-2000, a serwer odpowiedział tylko tym wycinkiem. Streaming wideo, wznawialne pobierania i synchronizacja plików oparta o HTTP — wszystko to opiera się na 206.

Decyzja: 200 vs 201 vs 204 dla POST

ScenariuszKodCiało
POST tworzy nowy zasób201 CreatedNowy zasób (lub samo ID) + Location
POST uruchamia pracę async, wynik nieprzygotowany202 AcceptedID zadania, URL do odpytywania
POST to akcja bez zasobu (np. /login)200 OKWynik akcji (token, status)
POST kończy się sukcesem, ale odpowiedź jest pusta204 No Content(brak)

Jeśli nie możesz zdecydować między 200 a 201, zapytaj: „czy serwer utworzył zasób, który ma teraz własny URL?”. Jeśli tak, użyj 201. Jeśli nie, użyj 200.

3xx — przekierowania (301 vs 302 vs 307 vs 308)

Przekierowania to najczęściej źle używana klasa. Różnice między 301, 302, 307 a 308 sprowadzają się do trzech ortogonalnych pytań: czy zmiana jest trwała, czy metoda jest zachowana i czy odpowiedź jest cache’owalna.

301 Moved Permanently

Zasób przeniesiony i nie wróci. Wyszukiwarki przenoszą link equity na nowy URL. Przeglądarki i CDN-y cache’ują 301 w nieskończoność: jeśli przekierujesz /old na /new z 301, a potem zmienisz zdanie, użytkownicy z cache’owanymi przekierowaniami będą iść do /new na zawsze (lub dopóki nie wyczyszczą cache).

Historycznie przeglądarki mogły przepisywać metodę żądania przy 301 (POST na GET), dlatego HTTP/1.1 wprowadziło 308, by to naprawić.

302 Found

Tymczasowe przekierowanie. Oryginalny URL nadal jest kanoniczny, więc wyszukiwarki powinny dalej indeksować oryginał. Użyj tego do routingu A/B test, stron konserwacyjnych lub flow „zaloguj się, by kontynuować”.

Podobnie jak 301, przeglądarki historycznie przepisywały POST na GET przy 302. Jeśli chcesz przekierować POST i zachować POST, użyj zamiast tego 307.

303 See Other

Zawsze przepisuje metodę na GET. Wzorzec Post/Redirect/Get: formularz wysyła POST do /submit, serwer zwraca 303 z Location: /thank-you, przeglądarka wykonuje GET /thank-you. Odświeżenie strony z podziękowaniem nie wyśle ponownie formularza. Do tego właśnie został zaprojektowany 303.

304 Not Modified

Odpowiedź warunkowa. Klient wysyła If-None-Match: "abc123" (lub If-Modified-Since), serwer sprawdza, czy zasób się zmienił, i jeśli nie, zwraca 304 bez ciała. Przeglądarka używa swojej kopii z cache. Tak działa każdy CDN i warstwa cache, która utrzymuje twoją stronę w szybkim stanie.

307 Temporary Redirect

Jak 302, ale metoda nie może się zmienić. POST pozostaje POST, ciało zachowane. Użyj, gdy chcesz tymczasowego przekierowania na żądaniu innym niż GET.

308 Permanent Redirect

Jak 301, ale metoda nie może się zmienić. Nowoczesny, bezpieczniejszy wybór dla trwałych przekierowań w API akceptujących POST/PUT.

Macierz decyzyjna: który kod przekierowania?

Trwałe (cache na zawsze)Tymczasowe (nie cache’uj)
Metoda może zmienić się na GET301 Moved Permanently302 Found
Metoda musi pozostać taka sama308 Permanent Redirect307 Temporary Redirect

Przypadek specjalny: jeśli konkretnie chcesz POST → GET (wzorzec Post/Redirect/Get), użyj 303 See Other.

Dla stron HTML z nawigacją przeglądarki 301 i 302 są zwykle w porządku, bo GET to GET. Dla API i formularzy preferuj 308 i 307, by uniknąć nieoczekiwanych zmian metody.

4xx — błędy klienta (jak wybrać właściwy)

4xx oznacza, że klient zrobił coś źle. Im więcej kodów 4xx rozróżniasz, tym łatwiej używać twojego API: klienci mogą rozgałęziać się po kodzie zamiast parsować stringi błędów.

400 Bad Request

Generyczny błąd składniowy. Niepoprawny JSON, brakujące wymagane pole na poziomie strukturalnym, request, którego serwer nie potrafi nawet sparsować. Jeśli request się parsuje, ale nie przechodzi walidacji biznesowej, preferuj 422.

401 Unauthorized vs 403 Forbidden

Najczęściej mylona para w HTTP. Podział jest prosty, gdy raz go zobaczysz:

  • 401 Unauthorized — request nie ma poprawnego uwierzytelnienia. Serwer nie wie, kim jesteś. Ponowne wysłanie credentials (lub odświeżenie tokenu) może to naprawić. Odpowiedź musi zawierać nagłówek WWW-Authenticate zgodnie z RFC 9110 §15.5.2.
  • 403 Forbidden — serwer wie, kim jesteś, i mimo wszystko odmawia. Ponowne wysłanie żądania nie pomoże. Potrzebujesz innych credentials lub innych uprawnień.
WidziszCo to znaczy
401 z WWW-Authenticate: BearerBrak tokenu, wygasły token lub niepoprawny token
403 po udanym logowaniuZalogowany, ale ten użytkownik nie ma dostępu do tego zasobu
401 po udanym logowaniuBug — prawdopodobnie chciałeś 403

Antywzorzec: 403-jako-404. Niektóre strony zwracają 403, gdy nieuwierzytelniony użytkownik prosi o /admin/dashboard. Ujawnia to istnienie /admin/dashboard. GitHub rozwiązuje to, zwracając 404 dla prywatnych repo, których nie jesteś członkiem; z twojej perspektywy zasób „nie istnieje”. To celowy wybór ukrywania informacji, a nie bug.

404 Not Found vs 410 Gone

Oba mówią „tego zasobu tu nie ma”. Różnica to trwałość i SEO.

  • 404 Not Found — może istnieje, może nie, może wróci. Wyszukiwarki będą dalej sprawdzać.
  • 410 Gone — był tu, celowo usunięty, nie wróci. Wyszukiwarki usuwają go z indeksu znacznie szybciej.

Jeśli usuwasz stronę produktu i chcesz ją wyrzucić z indeksu Google teraz, 410 jest właściwym wyborem. Jeśli URL jest po prostu chwilowo zepsuty, 404 wystarczy.

405 Method Not Allowed

URL istnieje, ale nie akceptuje tej metody. Odpowiedź musi zawierać nagłówek Allow z listą obsługiwanych metod.

HTTP/1.1 405 Method Not Allowed
Allow: GET, HEAD, OPTIONS
Content-Type: application/json

{"error": "POST is not allowed on this endpoint"}

Pominięcie nagłówka Allow to naruszenie kontraktu numer jeden w ręcznie pisanych API REST.

408 Request Timeout

Klient zaczął wysyłać request i potem zamilkł. Serwer się poddał. W odróżnieniu od 504 Gateway Timeout, który dotyczy upstream, 408 znaczy „ty, klient, potrzebowałeś za dużo czasu”.

409 Conflict

Request jest w konflikcie z bieżącym stanem. Najczęstsze użycie: optymistyczne blokowanie. Klient wysyła If-Match: "etag-v3", a bieżący ETag serwera to "etag-v4", więc aktualizacja zostaje odrzucona z 409.

410 Gone

Patrz wyżej: trwałe usunięcie. Przydatne do usuwania soft-deleted rekordów z indeksów wyszukiwania.

415 Unsupported Media Type

Klient wysłał ciało, którego serwer nie rozumie. POST z XML do API obsługującego tylko JSON dostaje 415. Odpowiedź powinna podpowiedzieć akceptowalne typy.

422 Unprocessable Content

Request parsuje się dobrze, ale nie przechodzi walidacji semantycznej. RFC 9110 w końcu w 2022 awansowało ten kod z WebDAV do core spec. Użyj 422 dla błędów walidacji:

{
  "error": "validation_failed",
  "details": [
    {"field": "email", "message": "must be a valid email"},
    {"field": "age", "message": "must be at least 13"}
  ]
}

Jeśli twoje API nie potrafi zdecydować między 400 a 422, prosta zasada brzmi: 400 dla „nawet nie potrafię tego sparsować”, 422 dla „sparsowałem i nie ma to sensu”.

425 Too Early

Wysyłany, gdy serwer nie chce ryzykować przetworzenia żądania, które może być replayem TLS 1.3 early-data. Głównie istotne dla CDN-ów i reverse proxy.

428 Precondition Required

Serwer nalega, byś wysłał If-Match lub If-Unmodified-Since w celu uniknięcia problemu utraconej aktualizacji. Używane w API edycji współpracującej.

429 Too Many Requests

Limit przekroczony. Odpowiedź musi zawierać Retry-After (w sekundach lub jako data HTTP), by dobrze zachowujący się klienci mogli się wycofać.

HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/json

{"error": "rate_limited", "limit": 100, "window": "1m"}

Numer to nawiązanie do Bradbury’ego. Przypadek użycia nie jest fikcyjny: takedowny DMCA, usunięcia w ramach prawa do bycia zapomnianym z RODO i geo-blocki na poziomie krajowym — każdy z nich uzasadnia 451. Odpowiedź powinna zawierać nagłówek Link wskazujący na organ prawny wymagający blokady, zgodnie z RFC 7725.

418 I’m a Teapot (easter egg)

Tak, jest prawdziwy. RFC 2324 (prima aprilis 1998) i IETF utrzymują go na liście, bo zbyt wiele produktów zaimplementowało go w żartach. Nie wysyłaj 418 w prawdziwym API; większość reverse proxy i load balancerów źle go obsłuży.

Macierz decyzyjna: który 4xx?

SytuacjaKod
Ciało jest niepoprawne lub nieparsowalne400
Brak uwierzytelnienia / wygasły token401
Uwierzytelniony, ale bez uprawnień403
URL nie istnieje (lub go ukrywasz)404
URL istniał, celowo usunięty410
Zła metoda HTTP405 (z Allow)
Zły Content-Type415
Konflikt optymistycznej blokady409
Błąd walidacji (parsuje się, nie waliduje)422
Limit przekroczony429 (z Retry-After)
Zablokowane z powodów prawnych451

5xx — błędy serwera (co naprawdę jest zepsute)

5xx to „nasza wina”. Inżynierowie on-call najbardziej zwracają uwagę na to, który 5xx ich wybudził o 3 nad ranem, bo kod mówi, którą warstwę zbadać najpierw.

500 Internal Server Error

Łapacz wszystkiego. Prawie zawsze oznacza, że nieobsłużony wyjątek wybąblował się do domyślnego handlera frameworka. Nic ci nie mówi o przyczynie — dlatego strukturalne logowanie liczy się tu bardziej niż kod statusu.

501 Not Implemented

Serwer w ogóle nie obsługuje metody. W odróżnieniu od 405 (ta metoda nie jest dozwolona dla tego URL), 501 mówi „ten serwer nie ma pojęcia, czym w ogóle jest PROPFIND”. Rzadkie w API REST.

502 Bad Gateway

Reverse proxy lub load balancer otrzymał niepoprawną odpowiedź od upstream. Upstream odpowiedział, ale śmieciem: zły protokół, niepoprawne nagłówki, urwane połączenie w środku odpowiedzi. Jeśli widzisz 502 z CDN, origin prawdopodobnie się crashuje albo zwraca obcięte ciała.

503 Service Unavailable

Serwer celowo w tej chwili nie obsługuje requestów. Użyj tego dla okien konserwacyjnych lub łagodnych odpowiedzi przy przeciążeniu. Powinien zawierać Retry-After.

504 Gateway Timeout

Reverse proxy czekało na upstream, a upstream nigdy nie odpowiedział na czas. Upstream jest powolny lub utknął; w odróżnieniu od 502, gdzie upstream odpowiedział śmieciem.

502 vs 504: diagnoza on-call

WidziszPierwsza rzecz do sprawdzenia
502 Bad GatewayUpstream odpowiada niepoprawnymi danymi: sprawdź logi origin pod kątem crashy, niepoprawnych odpowiedzi, niezgodności protokołów
504 Gateway TimeoutUpstream zawiesza się: sprawdź CPU origin, zapytania DB, wywołania downstream API i proxy_read_timeout proxy

Częste pomieszanie: zapytanie do bazy zajmujące 60 sekund pojawi się jako 504, jeśli proxy ma timeout 30 sekund, ale jako 500, jeśli serwer aplikacji ma timeout 90 sekund i rzuca wyjątek. Ta sama przyczyna źródłowa, inny kod, inna linia logu; naucz swoje dashboardy pokazywać oba.

507 Insufficient Storage

Specyficzny dla WebDAV. Dysk pełny na serwerze. Jeśli widzisz to z API niebędącego WebDAV, ktoś nadużywa znaczenia.

508 Loop Detected

Nieskończona rekurencja w operacjach WebDAV PROPFIND. Bardzo rzadkie.

511 Network Authentication Required

Kod captive portal: WiFi w hotelu lub na lotnisku wysyła 511, żeby powiedzieć przeglądarce „musisz najpierw zalogować się do portalu”. Odpowiedź zawiera Location na stronę portalu.

Macierz troubleshootingu: którą warstwę sprawdzić najpierw

KodAppProxyDBNetwork
500TakMoże (nieprzechwycony błąd DB)
502Tak (upstream zniekształcony)Może (TCP reset)
503Tak (flaga konserwacji)Tak (odrzucenie rate-limit)
504Tak (powolny handler)Tak (config timeoutu)Tak (powolne zapytanie)Tak (DNS, utrata pakietów)

Częste antywzorce kodów statusu HTTP

Tych pięć błędów odpowiada za większość złego kodu, który przeglądam.

1. Opakowywanie błędów w 200 OK

HTTP/1.1 200 OK
{"success": false, "error": "user_not_found"}

Każde narzędzie monitoringu, CDN i cache myśli teraz, że request się powiódł. Logika retry zawodzi. Load balancery świadome kodów statusu kierują zły ruch do „zdrowych” backendów. Ten wzorzec przyszedł z JSON-RPC i został odziedziczony przez GraphQL; GraphQL robi tak, bo częściowe sukcesy wymagają raportowania błędów per pole, co jest fair. REST nie ma wymówki: użyj 4xx dla błędów klienta, 5xx dla błędów serwera, a strukturalne szczegóły umieść w ciele.

2. Mieszanie 401 i 403

Jeśli twoje 401 i 403 nie są spójne, atakujący mogą próbkować twoje API, by odkryć, które zasoby istnieją. Wybierz politykę: albo zwracaj 404 dla „nie możesz tego zobaczyć” (podejście GitHuba dla prywatnych repo), albo zwracaj 403 konsekwentnie. Niespójność ujawnia informacje.

3. Ukrywanie 403 za 404

Czasem poprawne, często bug. GitHub zwracający 404 dla prywatnych repo jest celowy — samo istnienie repo jest wrażliwą informacją. Ale jeśli twoje API zwraca 404 dla „to konto użytkownika jest zawieszone”, to prawowici użytkownicy nie odróżnią pomyłki w nazwie użytkownika od zawieszenia. Udokumentuj swoją politykę jawnie i stosuj ją konsekwentnie.

4. Używanie 500 jako domyślnego łapacza

Frameworki to ułatwiają, co jest właśnie problemem. Każdy nieprzechwycony wyjątek staje się 500 i alerting nie odróżnia „baza padła” od „użytkownik wysłał niepoprawny UUID”. Łap błędy walidacji i podnoś 400 lub 422. Łap NotFound z ORM i podnoś 404. Zarezerwuj 500 dla naprawdę nieoczekiwanych awarii, a gdy je podniesiesz, loguj request ID, by móc skorelować.

5. Długie łańcuchy przekierowań

Każdy hop kosztuje round trip. Jeśli /old/intermediate/canonical, to dwa dodatkowe lookupy DNS i dwa dodatkowe handshake’i TCP (najgorszy przypadek). Google specjalnie obniża priorytet crawla dla łańcuchów dłuższych niż 3 hopy, a przeglądarki ograniczają łańcuchy przekierowań do około 20, by zapobiec pętlom. Złóż łańcuchy u źródła: w konfiguracji CDN albo w mapie przekierowań aplikacji.

Kody statusu HTTP a SEO

Wyszukiwarki traktują kody statusu jako autorytatywne sygnały, czy zachować, usunąć lub przenieść URL. Pomyl je, a twoje rankingi się przesuną.

301 Moved Permanently przenosi PageRank: Google traktuje nowy URL jako kanoniczne miejsce docelowe wszystkich sygnałów wskazujących na stary URL. 302 Found nie przenosi link equity (lub przenosi je powoli, w zależności od heurystyk Google). Jeśli przemianowałeś URL na stałe, użyj 301. Jeśli przekierowujesz gościa do /login, użyj 302.

404 vs 410 vs Soft 404

Google rozróżnia trzy stany „brakujące”:

  • 404 Not Found — Google sprawdza ponownie okresowo i trzyma URL w indeksie przez jakiś czas.
  • 410 Gone — Google usuwa URL szybciej, często w jednym cyklu crawla.
  • Soft 404 — termin Google dla strony, która zwraca 200 OK, ale renderuje komunikat „nie znaleziono”. Google wykrywa to po wzorcach treści i traktuje jak 404, ale zmarnowałeś request crawla i być może rozcieńczyłeś prawdziwą treść.

Jeśli porządkujesz przestarzały indeks, zwracaj prawdziwe 410 dla trwale usuniętych URL-i.

5xx i crawl budget

Crawler Google zmniejsza tempo, gdy strona zwraca uporczywe 5xx. Raport Crawl Stats w Search Console to pokazuje: utrzymujący się wzrost błędów 5xx może obniżyć twój crawl budget na dni, co oznacza, że nowe strony będą dłużej indeksowane. Traktuj wskaźniki 5xx jako metrykę SEO, nie tylko niezawodności.

200 OK, które tak naprawdę jest zepsute

Zwracanie 200 OK ze stroną błędu (antywzorzec soft-404) to najgorszy przypadek dla SEO. Google indeksuje komunikat błędu, rankuje go za nic i powoli orientuje się, że strona jest zepsuta. Zawsze zwracaj poprawny kod statusu z serwera, nawet jeśli twoja aplikacja SPA renderuje przyjazny UI błędu.

Jak sprawdzać kody statusu HTTP (narzędzia)

Nie naprawisz tego, czego nie widzisz. Każdy programista powinien biegle posługiwać się przynajmniej trzema z tych narzędzi.

Browser DevTools — panel Network

Chrome, Firefox i Safari pokazują kolumnę Status w zakładce Network. Kliknij prawym przyciskiem nagłówek kolumny, by dodać Status Text, jeśli nie jest widoczna. Przydatne sztuczki:

  • Preserve log — zachowuj wpisy między nawigacjami, by zobaczyć cały łańcuch przekierowań.
  • Filter by status — wpisz status-code:5xx (Chrome), by zobaczyć tylko błędy serwera.
  • Replay XHR — kliknij prawym przyciskiem dowolny request → Replay XHR, by uruchomić go ponownie bez przeładowania strony.

Dla przekierowań rozwiń request, by zobaczyć każdy hop i kod statusu na każdym kroku.

curl (uniwersalna odpowiedź)

curl pokazuje wszystko. Trzy wzorce, które rozwiązują 90% debugowania:

# Just the status code
curl -o /dev/null -s -w "%{http_code}\n" https://api.example.com/users/1

# Headers only (HEAD request, follow redirects)
curl -I -L https://example.com

# Full verbose with request and response headers
curl -v https://api.example.com/users/1

Gdy konstruujesz testowe URL-e ze znakami specjalnymi w query stringach, użyj --data-urlencode, by curl załatwił kodowanie za ciebie, lub wklej URL do naszego narzędzia Koder i dekoder URL, by zweryfikować, jakie bajty faktycznie pójdą po drucie.

# curl encodes the query value for you
curl -G "https://api.example.com/search" \
  --data-urlencode "q=hello world & friends"

# Sends: GET /search?q=hello%20world%20%26%20friends

JavaScript fetch

Właściwość Response.status zawiera całkowity kod. Response.ok ma wartość true dla każdego 2xx.

const res = await fetch('https://api.example.com/users/1');

console.log(res.status);      // 200
console.log(res.statusText);  // "OK"
console.log(res.ok);          // true

if (!res.ok) {
  if (res.status === 401) {
    // refresh token and retry
  } else if (res.status === 429) {
    const retryAfter = Number(res.headers.get('Retry-After')) || 1;
    await new Promise(r => setTimeout(r, retryAfter * 1000));
  } else if (res.status >= 500) {
    throw new Error(`Server error: ${res.status}`);
  }
}

W axios ta sama logika żyje w interceptorach:

import axios from 'axios';

axios.interceptors.response.use(
  response => response,
  error => {
    const status = error.response?.status;
    if (status === 401) {
      // redirect to login
    }
    return Promise.reject(error);
  }
);

Python requests

import requests

r = requests.get('https://api.example.com/users/1')

print(r.status_code)  # 200
print(r.reason)       # 'OK'

# Raises requests.exceptions.HTTPError for 4xx/5xx
r.raise_for_status()

# Manual handling
if r.status_code == 429:
    retry_after = int(r.headers.get('Retry-After', '1'))
    time.sleep(retry_after)
elif 500 <= r.status_code < 600:
    raise RuntimeError(f'Server error: {r.status_code}')

raise_for_status() to pythonowy idiom na „zawiedź głośno przy 4xx/5xx”. Używaj w skryptach, gdy chcesz wyjątków przy błędach zamiast rozgałęziania na status_code.

Postman i Bruno

Oba pozwalają asercjować na kodach statusu wewnątrz skryptu testowego:

// Postman/Bruno test script
pm.test("Status is 201", () => {
  pm.response.to.have.status(201);
});

pm.test("Has Location header", () => {
  pm.expect(pm.response.headers.get('Location')).to.match(/^\/users\/\d+$/);
});

Uruchamiaj je przeciwko stagingowi w CI, by łapać naruszenia kontraktu przed produkcją.

FAQ

Jaka jest różnica między 401 a 403?

401 Unauthorized oznacza, że serwer nie wie, kim jesteś — twoje credentials są brakujące, wygasłe lub niepoprawne. 403 Forbidden oznacza, że serwer wie, kim jesteś, i mimo wszystko odmawia. Jeśli wysłanie innych credentials może to naprawić, użyj 401. Jeśli nie — użyj 403.

Kiedy używać 301, a kiedy 302?

Użyj 301, gdy zmiana jest trwała: stary URL nigdy nie wróci, a chcesz, by wyszukiwarki przeniosły link equity na nowy URL. Użyj 302 dla tymczasowych przekierowań, gdy oryginalny URL jest nadal kanoniczny (flow logowania, testy A/B, strony konserwacyjne). Dla API preferuj 308 i 307, ponieważ zachowują metodę żądania.

Co oznacza błąd 502 Bad Gateway?

502 oznacza, że reverse proxy lub load balancer otrzymał niepoprawną odpowiedź od serwera upstream. Upstream odpowiedział, ale śmieciem: zły protokół, niepoprawne nagłówki lub urwane połączenie. Różni się od 504 Gateway Timeout, gdzie upstream w ogóle nie odpowiedział. Pierwsze miejsce do sprawdzenia: logi serwera origin pod kątem crashy lub obciętych odpowiedzi.

Czym jest „soft 404”?

„Soft 404” to strona, która zwraca 200 OK, ale faktycznie pokazuje komunikat „nie znaleziono”. Google wykrywa je heurystycznie i traktuje jak 404. Marnują crawl budget i mogą rozcieńczyć prawdziwą treść. Zawsze zwracaj prawdziwe kody statusu 404 lub 410 z serwera, nawet jeśli twoja aplikacja SPA renderuje przyjazny UI błędu.

Kiedy używać 422 zamiast 400?

Użyj 400 Bad Request, gdy serwer nie potrafi nawet sparsować żądania: niepoprawny JSON, brakujące pola strukturalne, błędy składniowe. Użyj 422 Unprocessable Content, gdy żądanie parsuje się dobrze, ale nie przechodzi walidacji biznesowej: niepoprawny format e-mail, wartość poza zakresem, semantycznie niespójne pola. Skrót: 400 dla składni, 422 dla semantyki.

Jak odpowiedzieć na 429 Too Many Requests?

Odczytaj nagłówek Retry-After (liczba sekund lub data HTTP) i wstrzymaj się przynajmniej przez tyle czasu przed ponowną próbą. Jeśli Retry-After brakuje, zastosuj wykładniczy backoff z jitterem startujący od około 1 sekundy. Nigdy nie ponawiaj natychmiast; w ten sposób dostaje się bana.

Czy kody informacyjne 1xx są nadal używane w 2026?

Tak, ale większość jest niewidoczna dla kodu aplikacji. 100 Continue i 101 Switching Protocols to bazowe funkcje HTTP/1.1. 103 Early Hints jest coraz częściej używane przez Cloudflare, Fastly i Vercel do wysyłania preload hints przed główną odpowiedzią; zauważalnie poprawia Largest Contentful Paint. Większość bibliotek HTTP zwija 1xx do finalnej odpowiedzi, więc zwykle widzisz je tylko w DevTools lub curl -v.

Czy 418 „I’m a teapot” to prawdziwy kod statusu?

Tak, co zaskakujące. RFC 2324 było żartem primaaprilisowym z 1998 roku, ale wystarczająco dużo produktów go zaimplementowało, że IETF utrzymało go na liście w RFC 7168. Nie wysyłaj 418 na produkcji: wiele reverse proxy i load balancerów źle go obsługuje, a poza żartem nie spełnia żadnego prawdziwego celu.

Powiązane artykuły

Zobacz wszystkie artykuły