502 Bad Gateway и 504 Gateway Timeout в Битриксе: как найти причину

Страницы «502 Bad Gateway» и «504 Gateway Time-out» показывает nginx, а не Битрикс. Это значит, что nginx не получил нормальный ответ от того, кому передал запрос: от Apache в BitrixVM или от PHP-FPM на своём сервере. Подпись «Bitrix Environment» под ошибкой появляется на серверах с веб-окружением Битрикса: на виртуальной машине BitrixVM или на сервере, где установлен пакет BitrixEnv.

Короткий ответ:

  • 502 — бэкенд не ответил как положено. Он не запущен или слушает не тот адрес, у nginx нет прав на сокет PHP-FPM, процесс PHP убили посреди запроса или заголовки ответа не поместились в буфер nginx.
  • 504 — бэкенд жив, но молчит дольше, чем разрешает proxy_read_timeout или fastcgi_read_timeout. По умолчанию это 60 секунд. Тот же 504 бывает, когда все процессы PHP заняты и запрос ждёт в очереди.

Причину почти всегда называет одна строка в error.log nginx за минуту ошибки. Ниже таблица этих строк, команды проверки и места, где менять настройки, чтобы их не перезаписало обновление BitrixVM.

Что означает «502 Bad Gateway / Bitrix Environment»

В BitrixVM 9 два веб-сервера: nginx принимает запросы и отдаёт статику, а PHP выполняет Apache (httpd), к которому nginx проксирует запрос. На своём сервере вместо Apache обычно стоит PHP-FPM, и nginx передаёт ему запрос по FastCGI. Схема одна: nginx спереди, бэкенд за ним.

Коды 502 и 504 описывают связь nginx с бэкендом: nginx не дождался ответа или не смог его принять. Поэтому перезапуск nginx сам по себе такие ошибки обычно не лечит: смотреть нужно на бэкенд.

Если PHP успел выполниться и упал с фатальной ошибкой, вы увидите не 502, а 500 или белый экран. Это другая ветка поиска: лог PHP. Как его включить, описано в заметке как включить логирование ошибок PHP в Битриксе.

Где взять строку с причиной

Откройте error.log nginx. Путь к нему записан в директиве error_log, проще всего найти его так:

nginx -T 2>/dev/null | grep -E '^\s*error_log'
tail -n 100 /var/log/nginx/error.log | grep -E 'upstream|connect\(\)'

Во второй команде указан стандартный путь пакетов nginx в большинстве дистрибутивов. Если файла там нет, возьмите путь из вывода первой команды.

Сопоставьте строку с таблицей:

КодСтрока в error.log nginxЧто случилосьГде смотреть дальше
502connect() failed (111: Connection refused) while connecting to upstreamбэкенд по HTTP (Apache в BitrixVM) не слушает портsystemctl status httpd, порт в proxy_pass
502connect() to unix:... failed (2: No such file or directory)PHP-FPM не запущен или сокет лежит по другому путиsystemctl status, listen в пуле и fastcgi_pass
502connect() to unix:... failed (13: Permission denied)у пользователя nginx нет прав на сокет PHP-FPMlisten.owner, listen.group, listen.mode
502recv() failed (104: Connection reset by peer) while reading response header from upstreamпроцесс PHP убили посреди запросалог PHP-FPM: execution timed out, exited on signal
502upstream sent too big header while reading response header from upstreamзаголовки ответа больше буфера nginxfastcgi_buffer_size или proxy_buffer_size
504upstream timed out (110: Connection timed out) while reading response header from upstreamбэкенд не прислал ни байта за таймаут чтениямедленный код, занятый пул, *_read_timeout

Что показал стенд: каждая причина отдельно

Чтобы строки в таблице были точными, все случаи воспроизведены на стенде: nginx 1.26.3, PHP-FPM 8.4.26 и встроенный сервер PHP в роли HTTP-бэкенда вместо Apache. Для nginx не важно, какая программа стоит за proxy_pass, важно только, отвечает ли она. Таймауты на стенде уменьшены до нескольких секунд, max_execution_time равен 2 секундам.

СценарийОтветВремя
Сокета PHP-FPM нет502сразу
Сокет с listen.mode = 0000502сразу
HTTP-бэкенд не слушает порт502сразу
sleep(6) при fastcgi_read_timeout 4s5044 с
sleep(6) при max_execution_time = 2 и таймауте nginx 20 с2006 с
Цикл без пауз при max_execution_time = 25002 с
sleep(6) при request_terminate_timeout = 3s5023,2 с
Заголовок ответа 6000 байт, буфер по умолчанию502сразу
Тот же заголовок при fastcgi_buffer_size 32k200сразу
pm.max_children = 1, второй запрос, пока первый спит 3 с, fastcgi_read_timeout 4s5044 с

Из таблицы видны три вещи, которые часто путают.

  1. max_execution_time не спасает от 504. В Linux PHP не считает время ожидания: sleep, ответ базы данных, запрос к внешнему API. Скрипт, который ждёт MySQL, PHP по max_execution_time не остановит. При настройках по умолчанию nginx отдаст 504 через 60 секунд. Это поведение описано в руководстве PHP к set_time_limit().
  2. Жёсткий лимит PHP-FPM даёт 502, а не 504. request_terminate_timeout убивает процесс, nginx видит разорванное соединение. Если в логе nginx Connection reset by peer, ищите в логе FPM строку execution timed out ... terminating.
  3. Занятый пул выглядит как медленный код. Запрос ждёт свободный процесс, и время ожидания входит в fastcgi_read_timeout. В логе FPM при этом есть предупреждение server reached pm.max_children setting.

502: бэкенд не запущен или слушает другой адрес

Так обычно выглядит сайт после перезагрузки сервера или обновления, если бэкенд не поднялся. В логе nginx Connection refused для HTTP-бэкенда или No such file or directory для сокета.

На BitrixVM проверьте Apache (команды выполняйте от root, иначе ss не покажет имена процессов):

systemctl status httpd
grep -R "proxy_pass" /etc/nginx/bx/conf/ /etc/nginx/bx/site_available/ | head
ss -ltnp | grep httpd

Порт, на который nginx отправляет запросы, берите из proxy_pass. Затем обратитесь к Apache напрямую, мимо nginx, подставив этот порт:

# 8888 - пример, подставьте порт из proxy_pass
curl -sI http://127.0.0.1:8888/ | head -n 1

Если Apache не стартует, причина будет в systemctl status httpd и journalctl -u httpd. Пример с форума разработчиков Битрикса: (98)Address already in use: AH00072: make_sock: could not bind to address 127.0.0.1:8888. Порт уже занят другим процессом, в той теме — оставшейся в памяти копией Apache. Найдите процесс через ss -ltnp, остановите его и запустите службу заново.

На сервере с PHP-FPM сверьте два пути: fastcgi_pass в nginx и listen в пуле PHP-FPM. В Debian и Ubuntu имя сокета содержит версию PHP, например /run/php/php8.4-fpm.sock. После смены версии сокет переезжает, а nginx продолжает обращаться к старому. Команды ниже — для путей Debian и Ubuntu:

nginx -T 2>/dev/null | grep fastcgi_pass
grep -R "^listen" /etc/php/*/fpm/pool.d/
ls -l /run/php/

Если Apache или PHP-FPM поднялся позже nginx, достаточно убедиться, что бэкенд отвечает напрямую, и повторить запрос. Перезагружать nginx нужно, только если вы меняли его конфиг.

502 с Permission denied: права на сокет PHP-FPM

Строка connect() to unix:/run/php/... failed (13: Permission denied) значит, что сокет есть, а пользователь, под которым работает nginx, не может в него писать. Права сокета задают три параметра пула: listen.owner, listen.group и listen.mode.

ls -l /run/php/php8.4-fpm.sock
ps -o user= -C nginx | sort -u

Пользователь nginx должен быть владельцем сокета или входить в его группу, и у этого владельца или группы должно быть право записи. Пример для пула:

listen = /run/php/php8.4-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

Это значения пула по умолчанию в пакете PHP 8.4 для Debian: там listen.mode закомментирован, и действует значение по умолчанию 0660. Подставьте своего пользователя nginx и перезапустите PHP-FPM: права выставляются при создании сокета.

502 после долгого запроса: процесс PHP убит

Если 502 появляется не сразу, а через десятки секунд, и в логе nginx recv() failed (104: Connection reset by peer) или похожее сообщение о закрытом соединении, процесс PHP завершился до ответа. Откройте лог PHP-FPM за ту же минуту:

WARNING: [pool term] child 2594025, script '.../slow.php' (request: "GET /term/slow.php?s=6") execution timed out (3.201516 sec), terminating
WARNING: [pool term] child 2594025 exited on signal 15 (SIGTERM) after 70.062828 seconds from start

Во второй строке 70 секунд — это возраст процесса, а не длительность запроса.

Это сработал request_terminate_timeout. Он нужен, чтобы зависший скрипт не держал процесс вечно, но если он меньше, чем реально работает тяжёлая страница админки, такая страница будет стабильно падать в 502. Удобнее держать его не меньше таймаута nginx: тогда долгий запрос закончится понятным 504, а не обрывом. Если в логе FPM exited on signal без строки execution timed out, процесс упал сам, и причину ищите в логе PHP и системном журнале.

502 «upstream sent too big header»: заголовки не влезли в буфер

nginx читает заголовки ответа в один буфер размером в одну страницу памяти, то есть 4 или 8 КБ. Если PHP прислал больше, nginx считает ответ некорректным и отдаёт 502. Раздувать заголовки могут многочисленные Set-Cookie, длинный Location при редиректе или отладочные заголовки модулей.

Размер поднимают вместе с fastcgi_buffers (для Apache — proxy_buffers). Если поднять только fastcgi_buffer_size, nginx 1.26.3 на стенде не пропустил конфиг:

nginx: [emerg] "fastcgi_busy_buffers_size" must be less than the size of all "fastcgi_buffers" minus one buffer

Рабочий вариант, проверенный на стенде:

# PHP-FPM
fastcgi_buffer_size 32k;
fastcgi_buffers 8 32k;

# Apache за nginx, как в BitrixVM
proxy_buffer_size 32k;
proxy_buffers 8 32k;

После правки выполните nginx -t и перезагрузите nginx. Если 502 не ушёл, значит, заголовки больше 32 КБ, и стоит выяснить, кто их отправляет. Посмотреть заголовки можно напрямую у бэкенда командой curl -sI на его порт.

504 Gateway Timeout: бэкенд думает дольше таймаута

504 со строкой upstream timed out (110: Connection timed out) while reading response header значит, что nginx ждал начало ответа дольше proxy_read_timeout (Apache) или fastcgi_read_timeout (PHP-FPM). Таймаут считается между двумя чтениями, а не на весь ответ, и по умолчанию равен 60 секундам. Сколько стоит у вас, покажет команда:

nginx -T 2>/dev/null | grep -E 'proxy_(read|send|connect)_timeout|fastcgi_read_timeout'

Какой запрос завис, видно по полю request в той же строке лога. На Битриксе это может быть:

  • тяжёлые операции админки: проверка и оптимизация таблиц, импорт, переиндексация, резервное копирование;
  • обмен с 1С, если шаг не укладывается в таймаут. Отдельный разбор — в статье обмен Битрикса с 1С: Allowed memory size и таймауты шага;
  • агенты на хитах: тяжёлый агент выполняется в запросе случайного посетителя. Как перенести их на cron, описано в статье агенты Битрикса и фоновые задачи;
  • медленный SQL-запрос или внешний HTTP-запрос без таймаута;
  • все процессы PHP заняты. Тогда в логе PHP-FPM есть server reached pm.max_children setting. Как считать размер пула, разобрано в статье PHP-FPM: настройка пулов, OPcache и preload.

Учтите, что PHP сам такой запрос не прервёт: время ожидания базы и сети в max_execution_time не входит. Поэтому увеличение max_execution_time от 504 не поможет. Таймаут nginx стоит поднимать, только если долгая операция ожидаема и редка.

Как поднять таймаут на BitrixVM, чтобы настройка не слетела

BitrixVM перезаписывает свои конфиги при обновлении и при изменении настроек через меню. Свои значения кладите только в места для собственных настроек, которые окружение не перезаписывает:

  • nginx для всего сервера: /etc/nginx/bx/settings/z_bx_custom.conf;
  • nginx для одного сайта: каталог /etc/nginx/bx/site_settings/<имя сайта>/;
  • Apache: /etc/httpd/bx/custom/z_bx_custom.conf;
  • PHP: /etc/php.d/z_bx_custom.ini.

Пример для nginx, если операция действительно идёт до пяти минут:

# /etc/nginx/bx/settings/z_bx_custom.conf
proxy_read_timeout 300s;
proxy_send_timeout 300s;
nginx -t && systemctl reload nginx
nginx -T 2>/dev/null | grep -E 'proxy_(read|send)_timeout'

Последняя команда должна показать ваши значения. Если там несколько строк с разными числами, проверьте, не задан ли таймаут ещё раз в location конкретного сайта: значение во вложенном блоке перекрывает общее. Таймаут больше, чем реально нужно, лучше не ставить: долгие запросы держат процессы бэкенда, и при наплыве посетителей свободных процессов не останется. Регулярную долгую работу надёжнее вынести в cron или фоновые задачи.

Как проверить, что исправлено

  1. Повторите запрос, который падал, и замерьте время: curl -s -o /dev/null -w '%{http_code} %{time_total}\n' https://ваш-сайт/путь/.
  2. Убедитесь, что в error.log nginx за это время нет новых строк с upstream.
  3. Для 504 сравните время ответа с таймаутом. Если запрос всё ещё близок к лимиту, ошибка вернётся при первой нагрузке, и медленное место нужно искать в коде или базе.
  4. Для 502 после правки буферов или прав проверьте и те страницы админки, на которых была ошибка, а не только главную.

Если страница ошибки выглядит иначе и не похожа на ответ nginx, например её показывает CDN или балансировщик перед сайтом, таймаут или ошибка соединения у него. Тогда сначала проверьте, отвечает ли сам сервер напрямую, мимо CDN.

Источники

Больше практических заметок по Битриксу и серверу — на karoche.ru.