nginx 502 Bad Gateway: przyczyny i rozwiązania

Opublikowano 14 min czytania

502 Bad Gateway oznacza, że nginx nie dostał od backendu poprawnej odpowiedzi. Pięć najczęstszych przyczyn, właściwy wiersz w logu błędów i sposób, w jaki udowodnisz, że poprawka naprawdę zadziałała.

Co naprawdę oznacza „502 Bad Gateway”

Błąd 502 nie pochodzi z twojej aplikacji, tylko z nginx. nginx przyjął żądanie, przekazał je dalej do backendu (PHP-FPM, Node, Python, inny serwer WWW) i nie dostał stamtąd użytecznej odpowiedzi. Dlatego na stronie błędu nie ma nic konkretnego.

Odróżnienie od sąsiednich kodów oszczędza w sytuacji awaryjnej mnóstwo czasu:

  • 500 Internal Server Error: backend odpowiedział, a odpowiedzią był błąd. Przyczyna leży w kodzie aplikacji. Czytaj log aplikacji, nie ten od nginx.
  • 502 Bad Gateway: połączenie z backendem w ogóle nie doszło do skutku albo zerwało się, zanim pojawiła się kompletna odpowiedź.
  • 504 Gateway Time-out: połączenie stało, backend po prostu za długo milczał, a nginx stracił cierpliwość.

To rozróżnienie jest najważniejszym punktem zaczepienia przy przekroczeniach czasu, bo ta sama wolna strona raz pojawia się jako 502, a raz jako 504, zależnie od tego, kto pierwszy przerwie. Więcej o tym poniżej.

Sytuacja pakietowa w Debianie 13, Debianie 12, Ubuntu 24.04 i Ubuntu 22.04

Przy błędach 502 nginx zachowuje się na wszystkich czterech systemach tak samo, dyrektywy nazywają się identycznie. Różnice tkwią niemal w całości po stronie PHP i właśnie stąd bierze się większość błędów 502 po zmianie dystrybucji.

SystemnginxPHPNazwa usługiSocket
Debian 13 (Trixie)1.26.38.4php8.4-fpm/run/php/php8.4-fpm.sock
Debian 12 (Bookworm)1.22.18.2php8.2-fpm/run/php/php8.2-fpm.sock
Ubuntu 24.04 LTS1.24.08.3php8.3-fpm/run/php/php8.3-fpm.sock
Ubuntu 22.04 LTS1.18.08.1php8.1-fpm/run/php/php8.1-fpm.sock

We wszystkich poniższych poleceniach zastąp numer wersji tym z twojego systemu. Wszystkie przykłady zakładają shell roota, w przeciwnym razie dopisz z przodu sudo. Która wersja jest zainstalowana, zdradzi rzut oka na pliki binarne FPM, nawet jeśli usługa w ogóle nie startuje:

ls /usr/sbin/php-fpm*
ls /etc/php/

Najpierw log błędów: jak znaleźć właściwy wiersz

Najczęstszy błąd w diagnostyce to szukanie w niewłaściwym logu. nginx ma globalny log błędów, a dodatkowo często osobny dla każdego wirtualnego hosta. Który plik obowiązuje, wynika z konfiguracji:

grep -Rn "error_log" /etc/nginx/nginx.conf /etc/nginx/sites-enabled/

Zwróć uwagę na wielkie -R. W katalogu /etc/nginx/sites-enabled/ na Debianie i Ubuntu leżą wyłącznie symlinki do sites-available, a GNU grep przy schodzeniu w głąb z małym -r nie podąża za żadnym symlinkiem. Z -rn dostaniesz więc tylko trafienia z nginx.conf, podczas gdy wiersz error_log należący do vhosta pozostanie niewidoczny: dokładnie ten, którego szukasz przy błędzie 502, bo globalny log nie zawiera błędu FastCGI, gdy tylko vhost przekierowuje logowanie gdzie indziej. Jeśli wolisz zostać przy -r, przeszukaj katalogi źródłowe bezpośrednio:

grep -rn "error_log" /etc/nginx/nginx.conf /etc/nginx/sites-available/ /etc/nginx/conf.d/

Bez własnego wpisu w bloku server wszystko ląduje w /var/log/nginx/error.log. Najpewniejsza droga do właściwego wiersza prowadzi przez podgląd na żywo: trzymaj log otwarty w jednym terminalu, w drugim wywołaj żądanie i obejrzyj wiersze, które w tym momencie dochodzą.

tail -f /var/log/nginx/error.log

Alternatywnie filtruj po znaczniku czasu. nginx zapisuje czas lokalny w formacie 2026/07/26 09:14:22, a nie w UTC. Porównanie z zegarem w innej strefie czasowej regularnie kończy się pomyłką.

Wiersz opisujący błąd 502 jest zawsze zbudowany według tego samego schematu. Przykład:

2026/07/26 09:14:22 [error] 812#812: *3 connect() to unix:/run/php/php8.2-fpm.sock
failed (2: No such file or directory) while connecting to upstream,
client: 203.0.113.7, server: example.com,
request: "GET /index.php HTTP/1.1",
upstream: "fastcgi://unix:/run/php/php8.2-fpm.sock:", host: "example.com"

Całą informację niosą cztery elementy:

  1. Wywołanie systemowe: connect(), recv(), send(). connect() oznacza, że połączenie nigdy nie doszło do skutku. recv() oznacza, że połączenie stało, a potem się zerwało.
  2. Numer błędu w nawiasie, patrz tabela poniżej. To jest właściwa diagnoza.
  3. Faza: while connecting to upstream wobec while reading response header from upstream. Pierwsze to problem z osiągalnością, drugie z czasem działania albo z awarią procesu.
  4. Pole upstream:. Stoi tam ścieżka albo adres, którego nginx naprawdę użył. Nie to, czego domyślasz się po konfiguracji, tylko to, co jest aktywnie załadowane.
KomunikatZnaczenieSekcja
2: No such file or directoryPlik socketu nie istniejeUsługa nie działa albo ścieżka jest błędna
13: Permission deniedSocket istnieje, ale nginx nie ma do niego dostępuUprawnienia
111: Connection refusedNic nie nasłuchuje na tym adresie i porcieBackend nieosiągalny
110: Connection timed outBrak odpowiedzi w wyznaczonym czasiePrzekroczenie czasu
104: Connection reset by peerProces backendu padł w trakcie obsługi żądaniaAwarie i limity
11: Resource temporarily unavailableKolejka socketu jest pełnaAwarie i limity

Wiersz z 13: Permission denied ma w nginx często poziom [crit] zamiast [error]. Kto filtruje wyłącznie po [error], ten go przeoczy. Lepiej filtruj po treści:

grep -n "upstream" /var/log/nginx/error.log

Druga połowa prawdy leży w logu PHP-FPM, domyślnie w /var/log/php8.2-fpm.log. Przy awariach i limitach to tam znajdziesz uzasadnienie, podczas gdy nginx widzi tylko objaw.

Przyczyna 1: PHP-FPM nie działa

Klasyczny numer błędu 2. Sprawdź najpierw stan usługi:

systemctl is-active php8.2-fpm
systemctl status php8.2-fpm --no-pager -l

is-active odpowiada jednym słowem, to w zupełności wystarczy skryptowi. Jeśli pojawi się inactive albo failed, wyciągnij powód z journala, i to z oknem czasowym zamiast ostatnich dziesięciu wierszy:

journalctl -u php8.2-fpm --since "30 min ago" --no-pager

Bardzo często przyczyną jest uszkodzona konfiguracja puli, która została w systemie po ostatnim przeładowaniu. FPM ma własny test składni, działający bez restartu:

php-fpm8.2 -t

Typowe błędy startu w oryginalnym brzmieniu i ich znaczenie:

  • ERROR: [pool www] cannot get uid for user 'webuser': użytkownik systemowy wpisany w user = już nie istnieje, na przykład po migracji.
  • ERROR: unable to bind listening socket for address '/run/php/php8.2-fpm.sock': No such file or directory (2): brakuje katalogu /run/php. Leży on na tmpfs i powstaje przy starcie usługi. Jeśli kierujesz listen na ścieżkę poza nim, musisz sam zadbać o utworzenie katalogu.
  • ERROR: An another FPM instance seems to already listen on ...: na sockecie wisi jeszcze proces z nieudanego restartu.

Po czym poznasz, że problem naprawdę zniknął: nie po tym, że systemctl restart przeszedł bez komunikatu. Proces główny FPM wystartuje również wtedy, gdy ani jeden proces roboczy nie jest w stanie przyjąć żądania. Miarodajne jest to, że socket jest widoczny w systemie i że FPM na nim odpowiada.

ss -lx | grep php

Do prawdziwego testu odpowiedzi włącz w /etc/php/8.2/fpm/pool.d/www.conf wiersz ping.path = /ping, przeładuj FPM i odpytaj socket bezpośrednio, całkowicie z pominięciem nginx:

apt-get install -y libfcgi-bin
SCRIPT_NAME=/ping SCRIPT_FILENAME=/ping REQUEST_METHOD=GET \
  cgi-fcgi -bind -connect /run/php/php8.2-fpm.sock

Jeśli wróci pong, strona PHP jest w porządku, a błąd leży między nginx a socketem. Jeśli nie wróci nic, w nginx nie ma po co dalej szukać.

Przyczyna 2: błędna ścieżka socketu

Na Debianie i Ubuntu to zdecydowanie najczęstsza przyczyna, bo socket nosi w nazwie numer wersji PHP, konfiguracja nginx wpisuje tę nazwę na sztywno, a aktualizacja dystrybucji rozjeżdża jedno z drugim.

Konkretnie: aktualizacja z Debiana 12 na Debiana 13 podnosi PHP z 8.2 na 8.4. Stary socket /run/php/php8.2-fpm.sock znika, a w pliku vhosta wciąż figuruje. Efektem jest błąd 502 na każdej pojedynczej stronie PHP, natychmiast po restarcie. To samo dzieje się przy przejściu z Ubuntu 22.04 na 24.04 (8.1 na 8.3).

Druga pułapka tkwi w dołączonej konfiguracji przykładowej. W pliku /etc/nginx/sites-available/default stoi zakomentowany blok, którego fastcgi_pass wskazuje na wersję PHP nieaktualną od lat. Kto tylko odkomentuje te wiersze, ten dopiero wtedy wygeneruje sobie błąd 502.

Porównaj obie strony. To, czego chce używać nginx:

grep -Rn "fastcgi_pass" /etc/nginx/

To, co FPM faktycznie udostępnia:

grep -n "^listen *=" /etc/php/*/fpm/pool.d/*.conf

Dodatek *= we wzorcu wyszukiwania jest celowy: wymaga po listen dowolnej liczby spacji, a potem znaku równości. Samo ^listen trafia bowiem także w listen.owner, listen.grouplisten.mode, przez co szukany wiersz ginie wśród trafień. Symbol wieloznaczny /etc/php/*/ jest natomiast jak najbardziej na miejscu, obejmuje każdą zainstalowaną wersję PHP. I wreszcie to, co istnieje w działającym systemie:

ls -l /run/php/

Wszystkie trzy wyniki muszą pokazywać tę samą ścieżkę. Ważne: grep -R po całym /etc/nginx/, a nie tylko po tym jednym pliku, który podejrzewasz. Fragmenty dołączane przez include to popularna kryjówka, podobnie jak stare pliki w sites-available, wciąż aktywne przez zapomniany symlink w sites-enabled. Również tutaj obowiązuje zasada: tylko wielkie -R podąża za tymi symlinkami i dopiero ono pokaże ci, który plik jest naprawdę aktywny.

ls -l /etc/nginx/sites-enabled/

Zanim cokolwiek zmienisz, zrób kopię. To kosztuje dwie sekundy, a w razie wątpliwości oszczędza odtwarzania z kopii zapasowej:

mkdir -p /root/backups
cp -a /etc/nginx/sites-available/default /root/backups/default.bak

Jeśli poprawiasz plik wielokrotnie, lepiej dołóż znacznik czasu (default.bak.$(date +%F-%H%M)), bo cp -a nadpisuje istniejący plik .bak bez słowa komentarza.

Po poprawce zawsze najpierw testuj, potem przeładowuj. reload zamiast restart, żeby istniejące połączenia się nie zerwały:

nginx -t
systemctl reload nginx

Pamiętaj: nginx -t sprawdza wyłącznie składnię. Ścieżka do socketu, która nie istnieje, uchodzi za całkowicie poprawną konfigurację. Zielone syntax is ok nie jest więc dowodem na to, że błąd 502 zniknął.

Przyczyna 3: uprawnienia do socketu

Numer błędu 13. Socket istnieje, nginx po prostu nie może go otworzyć. Na Debianie i Ubuntu nginx działa jako użytkownik www-data, a domyślna pula FPM zakłada socket odpowiednio do tego. Widać to w pliku puli:

grep -n "listen.owner\|listen.group\|listen.mode" /etc/php/*/fpm/pool.d/*.conf

Przy listen.owner = www-data, listen.group = www-data i trybie 0660 współpraca działa bez żadnej ingerencji. Zawieść może w trzech sytuacjach:

  • Własna pula na projekt. Jeśli usergroup wskazują na użytkownika projektu, listen.group musi mimo to być grupą, do której należy nginx. Typowe jest listen.owner = uzytkownikprojektu razem z listen.group = www-data.
  • Socket poza /run. Dostępny musi być nie tylko sam plik socketu: każdy katalog po drodze potrzebuje prawa wykonywania dla nginx. Socket w katalogu domowym z trybem 0700 nie jest osiągalny dla nikogo poza właścicielem.
  • nginx ze zmienionym user/etc/nginx/nginx.conf.

Podejrzenie potwierdzisz bez zgadywania, próbując dostępu dokładnie jako ten użytkownik, który potrzebuje go w normalnej pracy:

id www-data
sudo -u www-data test -w /run/php/php8.2-fpm.sock && echo "dostęp jest" || echo "brak dostępu"

Jeszcze bardziej miarodajne jest wywołanie cgi-fcgi z poprzedniej sekcji, również z sudo -u www-data z przodu. Jeśli FPM odpowiada jako root, ale nie jako www-data, diagnoza jest jednoznaczna.

Ustaw wartości w pliku puli, a nie przez chmod na pliku socketu. chmod 666 wytrzyma dokładnie do następnego restartu FPM, potem FPM zakłada socket na nowo z uprawnieniami z konfiguracji i błąd wraca, zwykle w najmniej dogodnym momencie.

Na Debianie i Ubuntu działa AppArmor. Dołączony profil dla nginx w stanie fabrycznym nie jest egzekwowany, ale mógł zostać uaktywniony przez szablony hardeningowe. Jeśli komunikat z numerem 13 utrzymuje się mimo poprawnych uprawnień, warto zajrzeć do aa-status oraz do journalctl -k | grep DENIED.

Przyczyna 4: przekroczenie czasu przy długich żądaniach

Tutaj porządna robota rozchodzi się ze zgadywanką, bo samo upłynięcie limitu nginx daje 504, a nie 502. Jeśli długie żądanie kończy się błędem 502, to prawie zawsze PHP-FPM sprzątnął wcześniej proces roboczy, a nginx zobaczył już tylko zerwane połączenie. W logu stoi wtedy zazwyczaj:

recv() failed (104: Connection reset by peer) while reading response header from upstream

Jednocześnie działają trzy limity, a o kodzie statusu decyduje ich kolejność:

  1. max_execution_timephp.ini, domyślnie 30 sekund w trybie FPM. Liczy wyłącznie czas działania skryptu. Czas oczekiwania w wywołaniach systemowych, na przykład na zawieszone zapytanie do bazy danych, pod Linuksem się nie wlicza. Dlatego akurat przy tym problemie, przy którym się jej spodziewasz, ta wartość nic nie da.
  2. request_terminate_timeout w pliku puli, fabrycznie wyłączony. Kończy proces roboczy twardo, niezależnie od tego, na czym ten wisi. To jest wartość, która produkuje błędy 502.
  3. fastcgi_read_timeout w nginx, domyślnie 60 sekund. Gdy upłynie, dostajesz 504.

Sensowna kolejność rośnie od środka na zewnątrz, żeby zawsze pierwsza zadziałała ta warstwa, która potrafi jeszcze wygenerować zrozumiały komunikat błędu. Na przykład 60, potem 75, potem 90 sekund. Przy odwrotnej kolejności dostaniesz błędy 502 zamiast czytelnych błędów PHP.

grep -rn "request_terminate_timeout" /etc/php/*/fpm/pool.d/*.conf

To, co FPM sprzątnął, stoi w jego logu czarno na białym:

WARNING: [pool www] child 1234, script '/var/www/html/import.php'
(request: "POST /import.php") execution timed out (76.271849 sec), terminating

Zanim podniesiesz limity, każ sobie pokazać, gdzie ucieka czas. FPM ma do tego własny log, który przy przekroczeniu zapisuje pełny stos wywołań PHP. Włącz go w pliku puli:

slowlog = /var/log/php-fpm-slow.log
request_slowlog_timeout = 5s

Po systemctl reload php8.2-fpm przy następnym wolnym żądaniu znajdziesz tam funkcję wraz z numerem wiersza, która wisi. W praktyce w czterech na pięć przypadków jest to zapytanie do bazy danych bez indeksu albo wywołanie zewnętrznego interfejsu programistycznego bez własnego limitu czasu. Podkręcanie limitów wydłuża wtedy tylko czas do wystąpienia błędu i dodatkowo blokuje procesy robocze.

Przyczyna 5: backend nieosiągalny

Dotyczy każdego backendu odpytywanego przez TCP: FPM na porcie 9000, aplikacji Node, usługi Java, kontenera. Charakterystyczny jest numer błędu 111.

connect() to 127.0.0.1:3000 failed (111: Connection refused) while connecting to upstream

Sprawdź najpierw, czy cokolwiek w ogóle nasłuchuje, a przede wszystkim na czym:

ss -ltnp

To polecenie wypisuje wyłącznie sockety TCP i stąd bierze się rozpowszechnione nieporozumienie: pula PHP-FPM w stanie fabrycznym nasłuchuje na sockecie uniksowym w /run/php/ i w tej liście nie pojawia się w ogóle, chociaż działa bez zarzutu. Widoczna staje się dopiero tak:

ss -lxn | grep php-fpm
ls -l /run/php/

Dla FPM polecenie ss -ltnp jest więc miarodajne tylko wtedy, gdy pula została świadomie przestawiona na TCP przez listen = 127.0.0.1:9000. Przy backendach w Node, Javie albo w kontenerze to natomiast dokładnie właściwe polecenie.

Trzy pułapki, które rzadko trafiają do poradników:

  • localhost rozwiązuje się najpierw na ::1. Jeśli w nginx stoi proxy_pass http://localhost:3000;, a aplikacja nasłuchuje tylko na 127.0.0.1, nginx próbuje adresu IPv6 i dostaje „Connection refused”. Usługa działa, port jest otwarty, a mimo to leci 502. Rozwiązanie: wpisz w nginx wprost 127.0.0.1 albo zwiąż aplikację z obiema rodzinami adresów.
  • nginx rozwiązuje nazwy jednorazowo, przy wczytywaniu konfiguracji. Jeśli w proxy_pass stoi nazwa hosta, nginx zapamiętuje adres. Gdy backend zmieni adres IP, na przykład po ponownym uruchomieniu kontenera, żądania lecą w próżnię aż do następnego reload.
  • Firewall na drodze powrotnej. Przy backendzie na innym serwerze pojawia się 113: No route to host albo przekroczenie czasu zamiast „Connection refused”. Sprawdź to przez ufw status i bezpośredni test połączenia z serwera nginx.

Jeśli używasz bloku upstream z kilkoma celami, dochodzi jeszcze osobny komunikat:

no live upstreams while connecting to upstream

Oznacza to, że po powtarzających się nieudanych próbach nginx wyłączył wszystkie cele z ruchu na czas fail_timeout. Nawet po naprawie backendu żądania zaczną przechodzić dopiero po upływie tego limitu. systemctl reload nginx natychmiast resetuje ten stan.

Błąd 502, który nie pasuje do żadnej z pięciu przyczyn

Dwa przypadki wyglądają jak awaria, choć nią nie są, i dlatego kosztują ponadprzeciętnie dużo czasu.

Zbyt duży nagłówek odpowiedzi. Aplikacja działa bez zarzutu, tylko pojedyncze żądania zwracają 502:

upstream sent too big header while reading response header from upstream

Wyzwalaczem są duże ciasteczka albo dane sesji w nagłówkach, a bufor nginx jest za mały. W bloku server albo location:

fastcgi_buffer_size 32k;
fastcgi_buffers 8 16k;
fastcgi_busy_buffers_size 64k;

Przy proxy_pass dyrektywy nazywają się proxy_buffer_sizeproxy_buffers. Typowe jest to, że problem dotyka tylko zalogowanych użytkowników, podczas gdy strona startowa ładuje się bez zarzutu.

Skończyły się procesy robocze. Pod obciążeniem w logu FPM pojawia się:

WARNING: [pool www] server reached pm.max_children setting (5), consider raising it

Nowe żądania czekają wtedy w kolejce socketu. Gdy i ona się zapełni, nginx zgłasza 11: Resource temporarily unavailable. Zanim podniesiesz pm.max_children, przelicz to na szybko: dostępna pamięć RAM podzielona przez rzeczywiste zużycie jednego procesu roboczego. Zbyt wysoka wartość zamienia błędy 502 na stan systemu, w którym kończy się pamięć, a to uderza również w bazę danych.

Awarie. Wiersze z exited on signal 11 (SIGSEGV) wskazują na uszkodzone rozszerzenie PHP, często po zmianie wersji PHP z pozostawionymi modułami z poprzedniego wydania.

Gdy poprawka nic nie daje: droga powrotna

Dwie zasady utrzymują szkody na niskim poziomie. Po pierwsze: jedna zmiana po drugiej, z kopią pliku oryginalnego w /root/backups, nigdy w katalogu WWW. Po drugie: sprawdzaj po każdym kroku, zamiast zmieniać trzy rzeczy naraz i potem nie wiedzieć, która pomogła.

Jeśli nginx po zmianie całkiem przestaje działać, przywróć kopię i przeładuj:

cp -a /root/backups/default.bak /etc/nginx/sites-available/default
nginx -t
systemctl reload nginx

Jeśli nginx po restart już nie startuje, systemctl status nginx rzadko mówi dość. Bardziej miarodajne:

journalctl -u nginx --since "10 min ago" --no-pager

Najczęstszym powodem nieudanego restart przy składniowo bezbłędnej konfiguracji jest zajęty port 80 albo 443, zwykle proces z poprzedniego uruchomienia. ss -ltnp | grep ':80' pokaże winowajcę.

Po czym poznasz, że problem naprawdę zniknął

Polecenie bez komunikatu o błędzie niczego nie dowodzi. systemctl reload milczy również wtedy, gdy merytorycznie nic się nie zmieniło, a nginx -t sprawdza tylko składnię. Wiarygodne są te cztery dowody:

  1. Odpytaj kod statusu bezpośrednio na serwerze, żeby ani cache, ani usługa stojąca przed nim nie zafałszowały wyniku:
    curl -sS -o /dev/null -w "%{http_code}\n" http://127.0.0.1/
    Oczekiwany jest kod 200, a nie 502. Przy kilku wirtualnych hostach podaj nazwę: curl -H "Host: example.com" ...
  2. Log błędów milczy. Wyczyść go przed testem przez truncate -s 0 /var/log/nginx/error.log, wywołaj kilka żądań, zajrzyj do niego ponownie. Pusty plik jest właściwym dowodem.
  3. FPM odpowiada z pominięciem nginx, prosto na sockecie, przez cgi-fcgi i jako www-data. Tym samym kwestia uprawnień i ścieżki jest załatwiona w jednym kroku.
  4. Restart niczego nie zmienia. Najważniejszy i najczęściej pomijany punkt. Wiele doraźnych działań (ręcznie ustawione uprawnienia, ręcznie założone katalogi w /run, usługa uruchomiona, ale nie włączona na stałe) nie przeżywa restartu. Sprawdź systemctl is-enabled php8.2-fpm nginx i uruchom serwer raz ponownie w kontrolowany sposób, dopóki jeszcze na to patrzysz, zamiast zostawiać to następnemu oknu serwisowemu.

Przy serwerach KVM Root Server i serwerach dedykowanych od KernelHost taki restart razem z dostępem do konsoli wykonasz w panelu klienta, nawet gdy usługa WWW akurat nie odpowiada. Serwery stoją w centrum danych maincubes we Frankfurcie nad Menem (TÜV TIER3+), podłączone do własnej sieci z ochroną DDoS. Dalsza lektura: nginx 504 Gateway Time-out: jak naprawić oraz PHP-FPM pm.max_children: jak poprawnie obliczyć.

Krótka lista kontrolna na sytuację awaryjną

  1. tail -f /var/log/nginx/error.log, wywołaj żądanie, zanotuj numer błędu.
  2. Numer 2 albo 111: czy usługa działa, czy ścieżka się zgadza. ss -lx | grep php zestawione z grep -Rn "fastcgi_pass" /etc/nginx/.
  3. Numer 13: uprawnienia w pliku puli, a nie przez chmod.
  4. Numer 104 albo 110: czytaj log FPM i slowlog, dopiero potem rozmawiaj o limitach czasu.
  5. Komunikat „too big header”: zwiększ rozmiary buforów.
  6. Po naprawie: wyczyść log, przetestuj ponownie, raz uruchom serwer od nowa.

Najczęstsze pytania

Dlaczego widzę 502, a nie 504, skoro strona jest po prostu wolna?
Gdy upłynie limit nginx (fastcgi_read_timeout, domyślnie 60 sekund), nginx odpowiada kodem 504 Gateway Time-out. Błąd 502 przy długich żądaniach powstaje zazwyczaj wtedy, gdy PHP-FPM wcześniej twardo kończy proces roboczy, najczęściej przez request_terminate_timeout. nginx widzi już tylko przerwane połączenie i zapisuje w logu 'recv() failed (104: Connection reset by peer)'. Ustaw limity rosnąco od środka na zewnątrz, a zamiast błędów 502 dostaniesz czytelne błędy PHP.
Po aktualizacji do Debiana 13 wszystkie strony PHP zwracają 502. Co muszę zmienić?
Socket nosi w nazwie numer wersji PHP. Debian 13 przynosi PHP 8.4, socket nazywa się /run/php/php8.4-fpm.sock, a w konfiguracji nginx stoi jeszcze ścieżka z Debiana 12 z wersją 8.2. Popraw fastcgi_pass we wszystkich aktywnych plikach vhostów, sprawdź poleceniami 'grep -Rn "fastcgi_pass" /etc/nginx/' oraz 'ls -l /run/php/', czy obie strony pokazują tę samą ścieżkę, i przeładuj potem nginx. Przy przejściu z Ubuntu 22.04 na 24.04 efekt jest ten sam (8.1 na 8.3).
Czy 'nginx -t' wystarczy jako dowód, że błąd zniknął?
Nie. 'nginx -t' sprawdza wyłącznie składnię konfiguracji. Ścieżka do socketu, która w systemie w ogóle nie istnieje, jest składniowo całkowicie poprawna, a mimo to przy każdym żądaniu daje 502. Wiarygodny jest dopiero test kodu statusu na samym serwerze, na przykład 'curl -sS -o /dev/null -w "%{http_code}\n" http://127.0.0.1/', razem z logiem błędów, który przy tym pozostaje pusty.
Jak znaleźć w logu błędów wiersz, który należy do mojego błędu 502?
Trzymaj 'tail -f /var/log/nginx/error.log' otwarte w jednym terminalu i wywołaj żądanie w drugim. Wiersze, które przy tym dochodzą, są tymi właściwymi. Pamiętaj, że wiele vhostów ma własny error_log, co sprawdzisz przez 'grep -Rn "error_log" /etc/nginx/nginx.conf /etc/nginx/sites-enabled/'. Wielkie -R jest tu decydujące, bo w sites-enabled leżą tylko symlinki, za którymi małe -r nie podąża, i wtedy właśnie wiersz error_log należący do vhosta pozostaje niewidoczny. Komunikaty o 'Permission denied' mają często poziom [crit] zamiast [error], dlatego lepiej filtruj po słowie 'upstream'.
Błąd 502 pojawia się tylko u zalogowanych użytkowników, strona startowa ładuje się normalnie. Z czego to wynika?
To prawie zawsze komunikat 'upstream sent too big header while reading response header from upstream'. Zalogowane sesje niosą większe ciasteczka i dodatkowe nagłówki, które rozsadzają bufor odpowiedzi nginx. Zwiększ fastcgi_buffer_size i fastcgi_buffers w bloku server albo location, a przy proxy_pass odpowiednio proxy_buffer_size i proxy_buffers.
Poprawiłem uprawnienia do socketu przez chmod, a po restarcie błąd wrócił. Dlaczego?
PHP-FPM zakłada plik socketu przy każdym starcie od nowa i ustawia przy tym wartości z listen.owner, listen.group i listen.mode z pliku puli. Ręczny chmod utrzyma się więc tylko do następnego startu usługi. Wpisz uprawnienia w /etc/php/<wersja>/fpm/pool.d/www.conf i przeładuj FPM.

nginx PHP-FPM 502 Bad Gateway Debian Ubuntu Rozwiązywanie problemów Serwer WWW Administracja Linuksem