Błąd 504 Gateway Timeout oznacza, że serwer pośredniczący nie doczekał się odpowiedzi od aplikacji, która miała wykonać pracę. Diagnozę zaczynasz w logu proxy, nie w przeglądarce - tam widać, na którym etapie skończyło się czekanie. Restart aplikacji bez tej wiedzy to zgadywanie.
Jeśli nie masz dostępu do serwera, zanotuj kod, adres i godzinę zdarzenia, a potem przekaż je osobie, która ten serwer utrzymuje. Co dokładnie zapisać - opisujemy niżej.
Co oznacza błąd 504 Gateway Timeout?
504 Gateway Timeout to kod odpowiedzi HTTP, którym serwer pośredniczący - gateway albo reverse proxy - informuje, że nie dostał na czas odpowiedzi od serwera nadrzędnego, czyli upstreamu. Kod wskazuje miejsce, w którym urwała się komunikacja, ale nie nazywa przyczyny wewnątrz aplikacji. Tak definiuje go RFC 9110, sekcja 15.6.5.
W tym układzie żądanie z przeglądarki trafia do proxy, a proxy przekazuje je dalej - do aplikacji, która ma je obsłużyć. Gdy odpowiedź nie wraca w wyznaczonym czasie, proxy przerywa czekanie i odsyła klientowi kod 504.
Zapamiętaj granicę tego kodu. 504 nie dowodzi awarii bazy, wyczerpania pamięci ani błędu w kodzie - mówi tylko, że ktoś po drugiej stronie proxy nie zdążył. Który to etap i dlaczego, ustalasz z logów, nie z komunikatu.
Minuta czekania i biały ekran
Zanim przejdziesz do komend, zobacz, jak ten błąd wygląda z obu stron ekranu.
Karol utrzymuje sklep internetowy klienta - aplikacja stoi za nginxem działającym jako reverse proxy. To fikcyjny przykład złożony z typowego przebiegu tej awarii, nie opis konkretnego wdrożenia. W poniedziałek księgowa klienta uruchomiła eksport zamówień z całego kwartału. Po dłuższej chwili czekania zamiast pliku zobaczyła stronę „504 Gateway Timeout" i zgłosiła, że sklep padł.
Karol wszedł na stronę główną - działała. Na wszelki wypadek zrestartował aplikację. Eksport uruchomiony ponownie skończył się tym samym błędem - i to po dokładnie takim samym czasie czekania, co za pierwszym razem. Dopiero wpis w logu nginx wyjaśnił sprawę: „upstream timed out while reading response header from upstream". Aplikacja przez cały czas pracowała i liczyła raport - ale dłużej, niż nginx był gotów czekać na jej odpowiedź.
Ta historia pokazuje dwie rzeczy, które oszczędzą ci godziny. Sklep „nie padł" - jedna operacja przekraczała limit, reszta działała. A restart niczego nie diagnozował: błąd wrócił, bo przyczyną nie był zawieszony proces, tylko czas wykonania jednej operacji. Zamiast zgadywać, przejdź poniższe kroki po kolei.
504 czy 502 - który to błąd?
Oba kody pochodzą z tej samej granicy między proxy a aplikacją, ale mówią co innego. RFC 9110 rozdziela je jednoznacznie, a nginx w logu odróżnia timeout od nieprawidłowej odpowiedzi.
| Kod | Co się stało | Gdzie szukać dalej |
|---|---|---|
| 504 Gateway Timeout | Odpowiedź nie przyszła na czas - proxy przerwało czekanie | Zostań na tej stronie i ustal fazę timeoutu |
| 502 Bad Gateway | Odpowiedź przyszła, ale była pusta albo nieprawidłowa | Przejdź do diagnozy błędu 502 Bad Gateway |
Nie rozstrzygaj tego po tym, jak długo kręciła się karta w przeglądarce. Czas odczuwany przez użytkownika nie jest kryterium - kryterium są kod odpowiedzi i wpis w logu proxy dla tego samego żądania.
Zapisz żądanie, zanim otworzysz logi
Log proxy z ruchliwego serwera to setki wpisów na minutę. Żeby znaleźć właściwy, zapisz cztery rzeczy o żądaniu, które skończyło się kodem 504:
- metodę HTTP (GET, POST...),
- host i pełną ścieżkę adresu,
- moment zdarzenia razem ze strefą czasową,
- czynność, która do błędu doprowadziła - u Karola „eksport zamówień z kwartału".
Tokenów, ciasteczek i danych z formularzy nie przepisuj do notatki ani do zgłoszenia - to sekrety, a do korelacji wpisów nie są potrzebne.
Znajdź to żądanie w logu proxy
Miejsce zapisu logu błędów wskazuje w nginx dyrektywa error_log - odczytaj z konfiguracji, dokąd trafiają wpisy i z jakim poziomem. Szukasz wpisu, który odpowiada zanotowanej metodzie, ścieżce i godzinie. Jeżeli konfiguracja dopisuje do logów $request_id, korelacja jest prostsza - sama dostępność tej zmiennej nie znaczy jednak, że ktoś dodał ją do używanego formatu logu.
Znaleziony wpis mówi więcej niż kod błędu: wskazuje, czy czekanie urwało się przy łączeniu z aplikacją, przy wysyłaniu do niej żądania, czy przy odczycie odpowiedzi. Ten jeden wpis opisuje jedno żądanie - nie stan całej aplikacji ani wszystkich adresów.
W której fazie skończyło się czekanie?
Proxy czeka w trzech różnych momentach i każdy ma w nginx osobny limit. Faza z logu mówi ci, co badać dalej.
| Fragment wpisu w logu | Dyrektywa nginx | Co się nie wydarzyło na czas |
|---|---|---|
| „while connecting to upstream" | proxy_connect_timeout |
nawiązanie połączenia z aplikacją |
| „while sending request to upstream" | proxy_send_timeout |
zapis żądania do aplikacji |
| „while reading response header from upstream" | proxy_read_timeout |
odczyt odpowiedzi aplikacji |
Dwa zastrzeżenia z dokumentacji nginx, które zmieniają interpretację. proxy_send_timeout i proxy_read_timeout ograniczają odstęp między kolejnymi operacjami zapisu albo odczytu, a nie całkowity czas transmisji - odpowiedź może płynąć dłużej niż limit, byle nie zamilkła na dłużej niż limit. A jeśli połączenie zostało wprost odrzucone i żadna odpowiedź HTTP nie powstała, to już nie jest 504 - taki przypadek diagnozujemy osobno przy błędzie ERR_CONNECTION_REFUSED.
Faza z logu u Karola brzmiała „while reading response header" - aplikacja przyjęła żądanie, ale nie zaczęła odpowiadać w limicie odczytu. Kolejny krok to sprawdzenie, co w tym czasie robiła.
Powtórz żądanie z pominięciem proxy
Teraz oddzielasz warstwy - wykonujesz tę samą operację bezpośrednio na adresie aplikacji, bez udziału proxy. Test uruchom na hoście, w kontenerze albo w tej przestrzeni sieciowej, z której proxy łączy się z aplikacją. Przestrzenie nazw sieci w Linuksie izolują interfejsy, routing, reguły zapory i gniazda - test z twojego laptopa może więc iść zupełnie inną drogą i niczego nie rozstrzygnąć.
Adres i protokół celu przepisz z dyrektywy proxy_pass w konfiguracji proxy:
curl -sv OPCJE_ODTWORZENIA_ZADANIA \
'SCHEMAT_UPSTREAMU://HOST_UPSTREAMU:PORT_UPSTREAMU/SCIEZKA_TESTOWA'
Placeholdery zastąp wartościami badanego żądania: schemat, host i port z proxy_pass, ścieżka z twojej notatki, a OPCJE_ODTWORZENIA_ZADANIA to metoda, nagłówki i treść oryginalnego żądania - dla zwykłego GET bez treści ten fragment po prostu usuń. Test z innymi danymi opisuje inną operację niż ta, która zwróciła 504. Sekretów nie wpisuj do historii powłoki ani do materiału, który pójdzie w zgłoszeniu.
Wynik czytaj z kodu zakończenia curl: 6 znaczy, że nie udało się rozwiązać nazwy hosta, 7 - że nie doszło do połączenia, a 28 - że operacja przekroczyła limit czasu, o ile taki limit w tym wywołaniu obowiązywał (pokazana komenda sama go nie ustawia; znaczenia kodów opisuje dokumentacja curl, libcurl-errors).
Sprawdź usługę i jej dziennik
Test bezpośredni mówi, czy aplikacja odpowiada. Jej dziennik mówi, dlaczego nie odpowiadała. Na hoście usługi zacznij od stanu jednostki:
systemctl status NAZWA_USLUGI
NAZWA_USLUGI to jednostka systemd obsługująca badaną aplikację - systemctl status pokaże, czy proces działa, i ostatnie wpisy jej dziennika. Potem odczytaj dziennik od momentu sprzed błędu:
journalctl -u NAZWA_USLUGI --since "MOMENT_PRZED_BLEDEM"
Opcja -u filtruje wpisy jednej jednostki, --since ogranicza wynik od podanego momentu - dobierz go do zanotowanej godziny i strefy czasowej hosta. Szukasz zdarzenia pasującego do metody, ścieżki i czasu żądania. Dopiero taki wpis uprawnia do wniosków o kodzie aplikacji, bazie danych, kolejce albo zasobach - bez niego to hipotezy.
Kiedy podniesienie timeoutu jest naprawą?
Wyłącznie wtedy, gdy diagnoza potwierdziła, że długi czas operacji jest zamierzony i wynika z jej natury, a nie z awarii. Eksport kwartału zamówień u Karola ma prawo trwać długo - zawieszone zapytanie do bazy tego prawa nie ma. Podniesienie limitu w drugim przypadku nie naprawia błędu, tylko każe użytkownikowi dłużej na niego czekać.
Jeśli zmiana jest zasadna, podnieś limit tej fazy, którą wskazał log - połączenia, zapisu albo odczytu - zamiast wszystkich trzech na oślep. Wartość dobierz do najdłuższej uzasadnionej operacji w twoim środowisku, nie z cudzego przykładu konfiguracji. Cały układ dyrektyw warstwy pośredniczącej opisujemy w tekście o konfiguracji reverse proxy.
Przy operacjach pokroju raportów rozważ też zmianę po stronie aplikacji: zadanie w tle i powiadomienie o gotowym pliku zdejmuje problem limitu w ogóle. To decyzja dla osoby rozwijającej aplikację, ale warto ją zapisać w zgłoszeniu.
Jak potwierdzić, że problem zniknął?
Weryfikacja idzie tą samą drogą co diagnoza, tylko w obie strony:
- Powtórz test bezpośredni z przestrzeni sieciowej proxy - te same opcje, schemat, host i ścieżka.
- Wykonaj identyczne żądanie przez publiczny adres, czyli przez proxy.
- Sprawdź kody obu odpowiedzi oraz nowe wpisy w logu proxy i w dzienniku aplikacji.
Nie zmieniaj metody, nagłówków ani treści między testami - inne żądanie to inny wynik. Za zaliczone uznaj dopiero to, że badana operacja przechodzi obiema ścieżkami, a w logach nie przybywa wpisów o timeoutcie. Jeden udany test potwierdza jedną operację, nie zdrowie całego systemu.
Co możesz zrobić na cudzej stronie?
Bez dostępu do serwera nie odczytasz logu proxy, więc nie ustalisz fazy timeoutu - to nie jest błąd po twojej stronie. Możesz za to przygotować zgłoszenie, które realnie skróci właścicielowi diagnozę: kod 504, pełny adres bez danych wrażliwych, moment ze strefą czasową i czynność, która błąd wywołała.
Odświeżenie karty ma sens raz - jeśli operacja po prostu trwała za długo, druga próba może się zmieścić w limicie. Czyszczenie pamięci podręcznej i zmiana DNS nie dotykają komunikacji między proxy a aplikacją, więc tego błędu nie ruszą.
Pytania o 504 Gateway Timeout
Czy błąd 504 zniknie po odświeżeniu strony?
Może zniknąć, jeśli operacja jednorazowo trwała dłużej niż limit proxy - kolejna próba bywa szybsza i mieści się w czasie. Jeśli błąd wraca przy tej samej czynności, przyczyna jest po stronie serwera i odświeżanie nic nie zmieni. Zanotuj adres i godzinę, a potem zgłoś problem właścicielowi strony.
Czym różni się 504 od 502 Bad Gateway?
504 znaczy, że odpowiedź aplikacji nie przyszła na czas, a 502 - że przyszła, ale była pusta albo nieprawidłowa. Tak rozdziela je RFC 9110. Rozstrzygają kod odpowiedzi i wpis w logu proxy dla tego samego żądania, nie czas oczekiwania widziany w przeglądarce.
Czy zwiększenie timeoutu w nginx naprawia błąd 504?
Naprawia tylko wtedy, gdy długi czas operacji jest zamierzony - jak eksport dużego raportu - i log wskazał, której fazy dotyczy limit. Gdy przyczyną jest zawieszona aplikacja albo blokada w bazie, wyższy limit jedynie opóźnia ten sam błąd. Najpierw diagnoza fazy, potem ewentualna zmiana wartości.
Gdzie w nginx szukać przyczyny błędu 504?
W logu błędów wskazanym przez dyrektywę error_log - znajdź wpis odpowiadający metodzie, ścieżce i godzinie żądania. Fragment „while connecting", „while sending request" albo „while reading response header" mówi, która faza przekroczyła limit. Potem powtórz operację z pominięciem proxy i porównaj z dziennikiem aplikacji.
Czy restart aplikacji usuwa błąd 504?
Restart pomaga tylko wtedy, gdy proces faktycznie się zawiesił - a to trzeba potwierdzić w dzienniku, nie założyć. Gdy 504 bierze się z czasu wykonania konkretnej operacji, błąd wróci przy pierwszej próbie po restarcie. Zacznij od logu proxy i fazy timeoutu, restart zostaw na sytuację, w której dziennik pokazuje martwy proces.
Czy błąd 504 oznacza, że cała strona nie działa?
Nie - proxy zgłasza 504 dla konkretnego żądania, które nie doczekało się odpowiedzi. Pozostałe adresy mogą działać bez zarzutu, jak sklep z przykładu, w którym wykładał się tylko eksport zamówień. Dlatego diagnozę prowadzi się dla jednej operacji, a nie dla „całej strony".
Gdzie szukać dalej?
Pozostałe kody i komunikaty z tej granicy - między przeglądarką, proxy i aplikacją - zebraliśmy w przeglądzie błędów serwera i sposobów ich diagnozy.
Fakty w tym tekście pochodzą z dokumentacji źródłowych: RFC 9110 (sekcje 15.6.3 i 15.6.5), dokumentacji nginx dla modułu ngx_http_proxy_module (dyrektywy proxy_connect_timeout, proxy_send_timeout, proxy_read_timeout, error_log, zmienna $request_id), dokumentacji curl (libcurl-errors) oraz stron man Linuksa i systemd: network_namespaces(7), systemctl(1), journalctl(1).