Ошибки 502, 504, 403 и 500 в Nginx: причины и как чинить

Что означает каждый код, где искать причину в журнале Nginx, и типовые исправления для бэкендов на PHP-FPM, Node.js и Python. Ubuntu 24.04, Debian 12.

21 октября 2026 г.·5 мин чтения·nginx, php-fpm, systemd, debug

Код ошибки говорит, на каком участке пути запрос сломался. 502 и 504 — проблема между Nginx и приложением за ним, 403 — права и индексные файлы, 500 — ошибка внутри приложения или зацикленная конфигурация. Причина почти всегда уже написана в error.log, поэтому порядок один: узнать код, прочитать журнал, исправить, проверить.

Для экспериментов подойдёт любой VPS с Ubuntu или Debian; на tanaka.host сервер можно взять на день-два, пройти инструкцию и удалить без лишних трат.

Порядок диагностики

Код ответа:

curl -sI https://example.com | head -1

Причина в журнале Nginx (в соседнем окне, пока обновляете страницу):

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

Если у сайта свой error_log в блоке server, найдите его путь:

nginx -T 2>/dev/null | grep -n error_log

Если в строке ошибки есть слово upstream, виновато приложение за Nginx — смотрите его службу и порт:

systemctl status myapp --no-pager
journalctl -u myapp -n 50 --no-pager
ss -tlnp | grep 8098

Любую правку конфигурации Nginx применяйте только так, чтобы опечатка не уронила все сайты:

nginx -t && systemctl reload nginx

502 Bad Gateway

Nginx передал запрос бэкенду и не получил ответа или получил мусор.

Приложение не запущено. В журнале connect() failed (111: Connection refused) while connecting to upstream. Конфигурация вида:

location / {
    proxy_pass http://127.0.0.1:8098;
}

а ss -tlnp | grep 8098 пуст. Смотрите journalctl -u myapp — обычно падение из-за битого конфига или занятого порта. Исправьте и запустите, затем проверьте бэкенд напрямую, минуя Nginx:

systemctl start myapp
curl -I http://127.0.0.1:8098

Чтобы приложение поднималось само, добавьте в его unit:

[Service]
# Перезапускаем приложение, если оно упало
Restart=on-failure
# Ждём три секунды перед перезапуском
RestartSec=3
systemctl daemon-reload
systemctl enable myapp

Про unit-файлы подробнее — служба systemd на примере бота.

PHP-FPM: сокет не найден. В журнале connect() to unix:/run/php/php8.1-fpm.sock failed (2: No such file or directory). Это типично после обновления PHP: пакет стал php8.3-fpm, а в конфиге сайта остался старый путь.

ls /run/php/
sed -i 's/php8.1-fpm.sock/php8.3-fpm.sock/' /etc/nginx/sites-available/example.com
nginx -t && systemctl reload nginx

PHP-FPM: нет доступа к сокету. (13: Permission denied) while connecting to upstream. Пул запущен от другого пользователя, и сокет создан с правами, которые не пускают www-data. В файле пула (/etc/php/8.3/fpm/pool.d/<пул>.conf) владелец сокета должен совпадать с пользователем Nginx:

cd /etc/php/8.3/fpm/pool.d
printf 'listen.owner = www-data\nlisten.group = www-data\n' >> errors.conf
php-fpm8.3 -t && systemctl restart php8.3-fpm
ls -l /run/php/errors-fpm.sock

Здесь нужен именно restart, а не reload: сокет пересоздаётся при старте.

Слишком большие заголовки. upstream sent too big header while reading response header from upstream — приложение отдаёт много cookie или длинные заголовки. Увеличьте буферы:

location / {
    proxy_pass http://127.0.0.1:8098;
    proxy_buffer_size 16k;
    proxy_buffers 8 16k;
}

504 Gateway Time-out

Бэкенд жив, но не ответил за proxy_read_timeout (по умолчанию 60 с). В журнале upstream timed out (110: Connection timed out). Замерьте, сколько реально отвечает приложение:

curl -s -o /dev/null -w '%{http_code} через %{time_total} с\n' http://127.0.0.1:8093/slow

Правильное лечение — ускорить запрос (индекс в базе, кеш, вынести работу в очередь). Если запрос по своей природе долгий — экспорт отчёта, генерация — поднимите лимит для этого location:

location / {
    proxy_pass http://127.0.0.1:8098;
    proxy_read_timeout 120s;
}

Для PHP-FPM директива называется fastcgi_read_timeout. Учтите второй лимит — max_execution_time в php.ini (по умолчанию 30 с): если он сработает раньше, вместо 504 вы получите 500 с Maximum execution time exceeded в журнале PHP.

403 Forbidden

Нет индексного файла. Запрос на /, в папке нет index.html или index.php, а autoindex выключен — Nginx отдаёт 403, в журнале directory index of "/var/www/site/" is forbidden.

ls -la /var/www/site
echo '<h1>Сайт работает</h1>' > /var/www/site/index.html
curl -sI http://127.0.0.1/ | head -1

Если папка должна показывать листинг (раздача файлов), включите autoindex on; в location.

Нет прав на файл. open() "/var/www/site/index.html" failed (13: Permission denied). Узнайте, от кого работает Nginx, и посмотрите права на каждом уровне пути:

grep ^user /etc/nginx/nginx.conf
namei -l /var/www/site/index.html

namei покажет всю цепочку /var, /var/www, /var/www/site, файл — и сразу видно, где права обрезаны. Нормальные права: папки 755, файлы 644, владелец www-data:

chown -R www-data:www-data /var/www/site
find /var/www/site -type d -exec chmod 755 {} +
find /var/www/site -type f -exec chmod 644 {} +

Не ставьте 777. Это открывает запись в файлы сайта любому процессу на сервере, включая взломанный скрипт соседнего сайта. Если после 644/755 ошибка осталась, проблема в папке выше по пути — её покажет namei -l.

Закрыта папка на пути. Файл 644, но одна из родительских папок без x для остальных (например, 700 на /var/www/site). Nginx не может в неё войти:

namei -l /var/www/site/public/index.html
chmod 755 /var/www/site

500 Internal Server Error

Фатальная ошибка PHP. В журнале PHP Fatal error: Uncaught Error: Call to undefined function mb_strlen() — не хватает модуля. Ставим по имени:

apt install -y php-mbstring
systemctl restart php8.3-fpm

Так же лечатся curl_init (php-curl), simplexml_load_string (php-xml), imagecreate (php-gd), PDO ... could not find driver (php-mysql). Какой модуль нужен, обычно пишет установщик CMS.

Синтаксическая ошибка. PHP Parse error: syntax error, unexpected .... Проверить файл без запуска:

php -l /var/www/site/parse.php

Цикл внутренних перенаправлений. rewrite or internal redirection cycle while internally redirecting to "/index.html". Типичный конфиг для SPA:

location / {
    try_files $uri /index.html;
}

Если index.html в root отсутствует, Nginx ищет его, не находит, снова перенаправляет на него — и через 10 итераций сдаётся с 500. Проверьте, что файл есть и root указывает туда, куда вы думаете:

ls -la /var/www/site
echo '<h1>Сайт работает</h1>' > /var/www/site/index.html
curl -sI http://127.0.0.1/ | head -1

Шпаргалка

Код Строка в error.log Первое, что проверить
502 Connection refused systemctl status, ss -tlnp
502 No such file or directory + .sock ls /run/php/, версия PHP в конфиге
502 Permission denied + .sock listen.owner/listen.group в пуле
502 too big header proxy_buffer_size
504 upstream timed out время ответа бэкенда, proxy_read_timeout
403 directory index ... is forbidden есть ли index.html
403 open() ... Permission denied namei -l, права 755/644
500 PHP Fatal error недостающий модуль, php -l
500 internal redirection cycle существует ли файл из try_files

Чтобы ошибки у каждого сайта не смешивались, задайте отдельный журнал в блоке server:

error_log /var/log/nginx/example.com.log;

Базовая связка Nginx + PHP-FPM с нуля описана в статье Nginx, PHP-FPM и SSL, а чтобы узнавать о 502 раньше клиентов — Uptime Kuma.