Błąd 400, często opisywany skrótowo jako error 400, pojawia się wtedy, gdy serwer nie potrafi poprawnie odczytać żądania i odrzuca je jeszcze przed wykonaniem operacji. W praktyce oznacza to problem z adresem, nagłówkami, formatem danych albo sposobem, w jaki przeglądarka czy aplikacja wysyła request. Poniżej rozkładam ten kod na czynniki pierwsze: co naprawdę znaczy, jak znaleźć źródło i kiedy problem leży po stronie pośrednika, a nie użytkownika.
Najkrócej, kod 400 mówi, że żądanie trzeba poprawić, zanim serwer je wykona
- To status z grupy 4xx, więc problem dotyczy samego żądania, a nie ogólnej awarii serwera.
- Najczęściej winny jest format: zły JSON, niepoprawne kodowanie znaków, sprzeczne nagłówki albo uszkodzone dane formularza.
- Jeśli wyślesz identyczne żądanie ponownie, zwykle dostaniesz ten sam rezultat.
- W API 400 często oznacza, że backend odrzucił request już na etapie parsowania.
- Najlepsza diagnostyka zaczyna się od URL-a, nagłówków, body i logów po obu stronach.
Co naprawdę oznacza kod 400
Kod 400 należy do grupy błędów 4xx, czyli odpowiedzi sugerujących problem po stronie klienta. To nie znaczy, że użytkownik zawsze zrobił coś źle; oznacza raczej, że żądanie jest nieczytelne, sprzeczne albo niekompletne. Serwer zwykle odrzuca je na etapie parsowania, zanim przejdzie do logiki aplikacyjnej.
W praktyce taki status pojawia się przy źle zbudowanym URL-u, uszkodzonym JSON-ie, konflikcie nagłówków albo wtedy, gdy po drodze coś modyfikuje request w sposób, którego druga strona już nie rozumie. Ważny szczegół: jeśli klient wyśle to samo żądanie bez zmian, wynik zazwyczaj będzie identyczny.
To rozróżnienie jest istotne, bo od razu kieruje diagnostykę na request, a nie na ogólne stwierdzenie, że "serwer nie działa". Następny krok to sprawdzenie, co najczęściej psuje taki komunikat w realnych wdrożeniach.

Najczęstsze przyczyny po stronie przeglądarki, aplikacji i API
W raportach i logach najczęściej widzę cztery grupy problemów. One się powtarzają, bo dotyczą nie samej aplikacji jako całości, tylko pojedynczego żądania, które w którymś miejscu zostało źle złożone.
Błędny adres lub niepoprawne kodowanie znaków
Jeśli w adresie pojawiają się znaki specjalne, spacje albo znaki spoza ASCII, a aplikacja nie zakoduje ich poprawnie, serwer może uznać request za uszkodzony. To szczególnie częste przy ręcznym składaniu linków, parametrach wyszukiwania i przekazywaniu wartości do endpointów bez poprawnego percent-encodingu.
Niepoprawny JSON albo zły format formularza
W API bardzo często winny jest JSON, który wygląda dobrze "na oko", ale łamie reguły składni: brak przecinka, niezamknięty ciąg znaków, zły typ cudzysłowów albo body wysłane w formacie innym niż oczekiwany. Podobny problem pojawia się przy formularzach kodowanych jako application/x-www-form-urlencoded, gdy backend oczekuje application/json.
Konflikt nagłówków i problem z framingiem
Nagłówki potrafią zepsuć request równie skutecznie jak body. Klasyczny przykład to konflikt Content-Length i Transfer-Encoding, który tworzy niejednoznaczność co do długości wiadomości. To nie jest kosmetyka, tylko problem parsowania, więc serwer ma pełne prawo odrzucić takie żądanie kodem 400.
Przeczytaj również: Plik DWG - Otwórz bez AutoCAD, zrozum i uniknij błędów
Stan przeglądarki, przekierowania i pośrednicy po drodze
Jeśli błąd pojawia się tylko w jednej przeglądarce, jednej sieci albo po konkretnej serii przekierowań, sprawdzam pośredników: CDN, reverse proxy, WAF oraz stan witryny zapisany lokalnie w przeglądarce. W takich przypadkach sam backend może być poprawny, a problem leży na brzegu infrastruktury albo w danych, które klient próbuje odtworzyć z pamięci.
Gdy te cztery grupy odfiltrujesz, diagnostyka staje się dużo prostsza, bo zostaje już tylko konkretna ścieżka requestu do sprawdzenia.
Jak sprawdzić źródło problemu krok po kroku
Ja zaczynam od porównania żądania, które działa, z tym, które się wywala. To najszybszy sposób, żeby zobaczyć różnicę między "wydaje mi się" a konkretnym błędem w requestcie.
- Sprawdź dokładny adres i metodę HTTP. Czasem problemem jest nie GET, tylko przypadkowy POST, albo literówka w ścieżce.
- Otwórz request w narzędziach deweloperskich albo odtwórz go w
curlczy Postmanie. Dzięki temu widzisz surowe nagłówki i body, a nie tylko efekt w UI. - Zweryfikuj
Content-Type,Accept,AuthorizationiContent-Length. To cztery miejsca, w których najczęściej pojawia się rozjazd między tym, co wysyła klient, a tym, czego oczekuje serwer. - Jeśli to formularz, wyślij minimalny wariant danych. Im mniej pól, tym łatwiej wykryć, które konkretnie pole lub znak psuje request.
- Sprawdź odpowiedź serwera, a jeśli to możliwe, także logi backendu. Dobre API zwraca choćby krótkie
messagealbo identyfikator żądania, który prowadzi prosto do problemu. - Porównaj request z dokumentacją endpointu. Różnica w nazwie pola, formacie daty albo typie wartości bywa wystarczająca, żeby dostać 400.
Jeśli po tych krokach błąd nadal wraca, zwykle nie jest już "ogólny". Wtedy zostaje pytanie, czy winny jest użytkownik, aplikacja, czy warstwa pośrednia między nimi.
Kiedy 400 nie oznacza winy użytkownika
Choć 400 opisuje błąd po stronie klienta, w realnej architekturze "klient" nie zawsze oznacza człowieka przy klawiaturze. Za requestem mogą stać aplikacja mobilna, integracja zewnętrzna, reverse proxy, CDN albo firewall aplikacyjny. Jeśli któryś z tych elementów zmienia nagłówki, ucina body albo blokuje nietypowy format, użytkownik zobaczy 400 mimo że sam niczego ręcznie nie zepsuł.
Najbardziej praktyczny sygnał? Jedne urządzenia działają, inne nie; jedna sieć działa, inna nie; aplikacja backendowa nie zapisuje nawet śladu problematycznego żądania. Wtedy patrzę na warstwę pośrednią, a nie na sam endpoint.
Warto też pamiętać, że wiele systemów zwraca dodatkowe informacje w treści błędu. Jeśli pojawia się message, error, trace ID albo identyfikator żądania, to jest to najlepszy trop do przekazania zespołowi technicznemu.
Jak odróżnić 400 od podobnych kodów HTTP
Najwięcej pomyłek widzę między 400 a kilkoma sąsiednimi kodami. Z zewnątrz wyglądają podobnie, ale znaczą coś innego, a przy API ta różnica ma realne znaczenie.
| Kod | Co oznacza | Jak czytam ten sygnał |
|---|---|---|
| 400 Bad Request | Serwer nie potrafi poprawnie odczytać żądania | Błąd w składni, nagłówkach, URL-u albo payloadzie |
| 401 Unauthorized | Brak prawidłowego uwierzytelnienia | Trzeba się zalogować lub odświeżyć token |
| 403 Forbidden | Żądanie jest zrozumiałe, ale dostęp jest zablokowany | Użytkownik ma tożsamość, ale nie ma uprawnień |
| 404 Not Found | Zasób nie istnieje lub nie jest ujawniany | Adres jest zły albo endpoint został usunięty |
| 415 Unsupported Media Type | Zły typ treści |
Content-Type nie pasuje do body |
| 422 Unprocessable Content | Składnia jest poprawna, ale dane nie przechodzą walidacji | Problem leży w regułach biznesowych lub wartościach pól |
W praktyce różnica jest prosta: 400 mówi "żądanie jest źle zbudowane", 415 mówi "format nośnika nie pasuje", a 422 sygnalizuje, że format jest poprawny, ale dane nie przechodzą walidacji semantycznej. To drobiazg na papierze, ale ogromna oszczędność czasu przy integracjach API.
Jak ograniczyć takie błędy w przyszłości
Jeśli projektuję formularz albo API, staram się z góry usuwać dwuznaczność: jasno opisuję wymagane pola, pilnuję kodowania znaków i zwracam komunikaty, które mówią nie tylko "coś jest nie tak", ale dokładnie które pole zawiodło. Dzięki temu 400 staje się szybkim sygnałem naprawczym, a nie wielogodzinnym polowaniem na błąd.
- Waliduję dane po stronie klienta i serwera, ale nie ufam wyłącznie frontendowi.
- Ustalam jeden, jednoznaczny
Content-Typedla każdego endpointu. - Testuję kontrakty integracyjne, żeby wykrywać zmianę schematu zanim trafi na produkcję.
- Loguję
request IDi zwracam czytelne komunikaty walidacyjne. - Unikam ręcznego składania URL-i w wielu miejscach kodu, bo tam najłatwiej o literówkę lub zły encoding.
Jeśli taki błąd wraca regularnie, zwykle nie chodzi o jeden pechowy przypadek, tylko o lukę w walidacji, dokumentacji albo obserwowalności. Im szybciej ją zamkniesz, tym mniej czasu stracisz na gaszenie kolejnych 400-tek i tym spokojniej będzie działać cała ścieżka requestu.
