Błąd 504 Gateway Timeout oznacza, że serwer pośredniczący, np. Nginx, load balancer albo CDN, przekazał żądanie do aplikacji i nie doczekał się odpowiedzi w ustalonym czasie. Aplikacja zwykle nadal działa, tylko odpowiada za wolno, więc błąd 504 to przede wszystkim problem wydajności, a nie awarii. Najczęściej spotykam go przy eksportach, imporcie produktów, raportach i stronach, które w trakcie ładowania czekają na zewnętrzne API. W tym wpisie pokazuję, jak znajduję operację, która trwa za długo, i jak ją naprawiam, zamiast tylko wydłużać limity czasu.
Dlaczego serwer nie zdążył z odpowiedzią
Każda warstwa między użytkownikiem a aplikacją ma własny limit czasu. Błąd 504 zwraca ta, której limit skończy się pierwszy:
| Warstwa | Ustawienie | Wartość domyślna |
|---|---|---|
| Nginx jako proxy dla Node.js lub innej aplikacji | proxy_read_timeout | 60 s |
| Nginx z PHP-FPM | fastcgi_read_timeout | 60 s |
| PHP | max_execution_time i request_terminate_timeout w PHP-FPM | zależnie od konfiguracji hostingu, często 30 s |
| Cloudflare | czas oczekiwania na serwer źródłowy | 100 s, po przekroczeniu Cloudflare pokazuje błąd 524 |
Za przekroczeniem limitu stoi niemal zawsze jedna z czterech przyczyn: wolne zapytanie do bazy danych, długa operacja wykonywana w trakcie ładowania strony, np. generowanie PDF albo import, czekanie na zewnętrzne API, które przestało odpowiadać, albo przeciążony serwer, na którym żądania stoją w kolejce do wolnych procesów.
Jak diagnozuję i naprawiam błąd 504
1. Mierzę, ile trwa odpowiedź i która warstwa ją przerywa
curl -o /dev/null -s -w "status: %{http_code} TTFB: %{time_starttransfer}s razem: %{time_total}s\n" https://example.pl/raport/
Jeśli błąd pojawia się zawsze po tym samym czasie, np. po 60 sekundach, od razu wiem, który limit zadziałał. Jeśli strona za CDN zwraca błąd, a bezpośrednio z serwera odpowiada po kilkudziesięciu sekundach, problemem jest wolna aplikacja, a CDN tylko pierwszy przestaje czekać.
2. Znajduję wolny kod
W PHP-FPM włączam dziennik wolnych żądań. Po przekroczeniu ustawionego czasu PHP zapisuje w nim ślad wywołań, czyli dokładnie pokazuje, w której funkcji skrypt utknął:
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/slow.log
Równolegle włączam w MySQL lub MariaDB dziennik wolnych zapytań. Najczęściej znajduję w nim zapytania bez indeksu albo pobieranie danych w pętli, po jednym zapytaniu na każdy produkt. Typowe problemy z bazą danych w WordPressie i innych systemach opisuje wydajna komunikacja z bazą danych w CMS, a sposób przeszukiwania logów pokazuje analiza logów serwera.
3. Przenoszę długie operacje poza żądanie
Eksport tysięcy zamówień, generowanie raportu czy import produktów nie powinny się wykonywać w czasie, gdy przeglądarka czeka na odpowiedź. Takie zadania przenoszę do kolejki albo zadania uruchamianego w tle, a użytkownikowi pokazuję komunikat, że plik będzie gotowy za chwilę lub zostanie wysłany e-mailem. W WordPressie zamieniam też WP-Cron, uruchamiany przy wejściach odwiedzających, na prawdziwe zadanie systemowe cron.
4. Ustawiam krótkie limity dla zewnętrznych usług
Gdy strona pobiera dane z zewnętrznego API, np. stany magazynowe albo kursy walut, ustawiam w kodzie limit czasu połączenia na kilka sekund i obsługuję jego przekroczenie, np. pokazując dane z pamięci podręcznej. Bez tego jedna niedziałająca usługa zewnętrzna zatrzymuje procesy PHP do granicy limitu i blokuje całą stronę.
5. Wydłużam limity tylko tam, gdzie to uzasadnione
Zwiększenie fastcgi_read_timeout czy max_execution_time usuwa objaw, ale każdy proces czekający dłużej blokuje
miejsce dla innych użytkowników. Dlatego wydłużam limity wyłącznie dla konkretnych adresów, np. panelu importu, a nie dla całej strony.
Błąd 504 a szybkość strony i SEO
Błąd 504 to skrajny przypadek wolnego serwera. Zanim odpowiedź przekroczy limit, strona przez dłuższy czas odpowiada wolno, a długi czas oczekiwania na pierwszy bajt pogarsza LCP i całe Core Web Vitals. Googlebot traktuje 504 jak każdy błąd 5xx: przy powtarzających się błędach ogranicza indeksowanie. Różnicę między 504 a błędem 502 zapamiętuję prosto: 502 to zła odpowiedź albo zerwane połączenie, 504 to brak odpowiedzi w wyznaczonym czasie.
Pozostałe błędy HTTP
Każdy kod błędu wskazuje na inną warstwę, więc diagnozę zaczynam od innego miejsca: