Вы открываете свой сайт, а вместо страницы видите белый экран с надписью 502 Bad Gateway, 504 Gateway Time-out, 403 Forbidden или 500 Internal Server Error. Номер ошибки выглядит пугающе, но на самом деле он довольно точно подсказывает, на каком участке пути от браузера до сайта что-то сломалось, а настоящая причина записана в журнале Nginx одной строкой. В этой статье мы разберём, как устроен этот путь, научимся находить причину за пару минут и по очереди исправим каждую из четырёх ошибок на примерах, которые встречаются чаще всего. Все примеры мы воспроизвели на тестовом сервере с Debian 12, версия Nginx на момент проверки 1.22.1, PHP 8.2.34.
Как устроен путь от браузера до сайта
Когда посетитель открывает страницу, его браузер отправляет запрос на веб-сервер, то есть в Nginx. Если нужна обычная картинка или готовая HTML-страница, Nginx просто берёт файл с диска и отдаёт его. Но большинство сайтов состоит не только из файлов. Страницу нужно собрать, например обратившись к базе данных, и этим занимается отдельная программа, которую называют бэкендом. Это может быть PHP-FPM, выполняющий скрипты WordPress, или приложение на Python либо Node.js, которое слушает свой порт. Nginx в такой связке работает привратником. Он принимает запрос, передаёт его бэкенду, ждёт ответ и пересылает его браузеру. Поэтому две из четырёх ошибок в названии содержат слово «шлюз» (Gateway), ведь в этот момент Nginx стоит шлюзом между посетителем и приложением.
Первая цифра кода показывает, на чьей стороне неудача. Код 403 относится к семейству 4xx, то есть к запросам, которые нельзя выполнить в том виде, как они пришли. Nginx прекрасно понял, чего хочет браузер, но отказался отдавать ответ, потому что показывать нечего или у него нет прав прочитать нужные файлы. Коды семейства 5xx означают сбой на стороне сервера. Код 500 говорит, что при обработке запроса что-то сломалось, чаще всего в самом скрипте или приложении. Код 502 означает, что Nginx не получил от бэкенда внятный ответ, например потому, что бэкенд не запущен. Код 504 означает, что бэкенд на связи, но не успел ответить за отведённое время. Поэтому при любой из этих ошибок нужно сначала понять, на каком звене цепочки произошёл обрыв.
Сам код называет звено, но не причину, и причину приходится искать в журнале. Рассмотрим, например, ситуацию, в которой остановился PHP-FPM. Браузер по-прежнему обращается к Nginx, тот пытается передать запрос в сокет PHP-FPM, но подключиться не может, и поэтому сам рисует короткую страницу 502 с номером своей версии внизу, а в журнал записывает, к чему именно он не смог подключиться. Если же PHP-FPM работает, но скрипт упал с фатальной ошибкой, Nginx получает от него ответ с кодом 500 и пустым телом и честно передаёт его дальше, а надпись об ошибке рисует уже сам браузер. Мы видели обе картины на тестовом сервере, и по внешнему виду страницы можно догадаться, кто её выдал, Nginx или приложение. Тем не менее точный ответ лежит в журнале ошибок, к которому мы и перейдём.
Что понадобится
- Сервер с Debian 12, на котором уже работает Nginx, например тариф DE-RZ9-1. Примеры из статьи очень лёгкие, а подходящий тариф для вашего сайта поможет выбрать статья про VPS. Если сервера ещё нет, закажите его по инструкции.
- Подключение по SSH, например через Termius. Команды выполняются от имени
root. - Nginx и PHP-FPM. Как их установить, мы описали в статье про установку Nginx. PHP-FPM нужен только для примеров с PHP.
- Сайт или приложение, на котором вы видите ошибку.
Адреса и имена в примерах
На тестовом сервере каждый пример работал на своём порту, поэтому в командах и на скриншотах терминала вместо домена указан адрес вида 127.0.0.1:8091. На ваш сайт ссылайтесь по домену, например https://example.com. Папки errors-demo-… и служба errors-app тоже придуманы для статьи, у вас будут свои имена. Скриншоты браузера сняты с тех же тестовых сайтов, домен example.com в адресной строке условный.
Шаг 1. Узнайте код ответа
Начните с команды curl. Она обращается к сайту так же, как браузер, но показывает только то, что действительно ответил сервер. Параметр -I просит прислать только заголовки ответа, а код стоит в самой первой строке.
# Показываем заголовки ответа, в первой строке будет код
curl -I https://example.comЕсли нужна только строка с кодом, добавьте параметр -s и передайте вывод команде head.
# Только первая строка ответа
curl -sI https://example.com | head -1Строка вида HTTP/1.1 502 Bad Gateway подтверждает, что ошибку выдал сервер, а не ваша сеть или браузер. Заодно в заголовке Server видна версия Nginx.
Шаг 2. Прочитайте журнал ошибок Nginx
Каждый отказ Nginx записывает в файл /var/log/nginx/error.log. Откройте журнал в режиме слежения, а затем в браузере обновите страницу с ошибкой. Новая строка появится в тот же момент.
# Следим за журналом, выход по Ctrl+C
tail -f /var/log/nginx/error.logСтарые строки не мешают
Команда tail -f сначала показывает последние десять строк журнала и только потом ждёт новых. Чтобы старые записи не отвлекали, добавьте параметр -n 0, как на скриншоте ниже.

Код ответа и строка из журнала, которая объясняет причину
Разобрать такую строку несложно. Возьмём ту, что мы получили, когда приложение за Nginx было остановлено.
2026/10/21 14:53:33 [error] 124894#124894: *3 connect() failed (111: Connection refused) while connecting to upstream, client: 127.0.0.1, server: example.com, request: "GET / HTTP/1.1", upstream: "http://127.0.0.1:8098/", host: "example.com"- Время и уровень в начале строки. Слово в квадратных скобках показывает серьёзность записи, у нас встречались
[error]и[crit]. - Суть проблемы сразу после номера процесса. Здесь это
connect() failed (111: Connection refused), то есть Nginx не смог подключиться, потому что порт закрыт. - client показывает, кто обратился, server и host показывают, к какому сайту, а request содержит сам запрос.
- upstream показывает, куда именно Nginx пытался передать запрос. Это адрес бэкенда, и он подсказывает, какую программу проверять дальше.
Журнал общий для всех сайтов на сервере, поэтому при большом трафике нужную запись удобно искать по адресу страницы.
# Последние записи, в которых встречается нужный адрес
grep '/slow' /var/log/nginx/error.log | tail -n 5Подробнее о том, как читать логи и где они лежат, рассказано в статье про логи в Linux.
Шаг 3. Проверьте программу за Nginx
Если в строке журнала есть слово upstream, дальше нужно выяснить, что происходит с бэкендом. Для приложения, которое работает как служба systemd, проверьте её состояние и последние сообщения. Вместо myapp подставьте имя вашей службы, а для PHP это будет php8.2-fpm.
# Состояние службы
systemctl status myapp --no-pager
# Последние 50 сообщений службы
journalctl -u myapp -n 50 --no-pager
# Кто слушает нужный порт, вместо 8098 подставьте свой
ss -tlnp | grep 8098В строке Active должно быть active (running). Значения failed и inactive (dead) означают, что программа остановилась, а причину обычно рассказывает журнал службы. Последняя команда показывает программы, которые слушают порты. Пустой ответ значит, что на этом порту никто не слушает и Nginx некому передавать запросы. Как управлять службами и читать их логи, подробно описано в статье про службы systemd.

Служба в состоянии failed, в журнале причина, порт 8098 никто не слушает
На скриншоте служба errors-app упала сразу после запуска. В журнале видна причина, FileNotFoundError, то есть приложению не хватило файла настроек. Зная номер ошибки и строку из журнала, можно переходить к разбору конкретной причины.
Ошибка 502 Bad Gateway
Страница 502 означает, что Nginx принял запрос, но не смог получить ответ от бэкенда. Она выглядит как короткая белая страница с номером версии Nginx внизу.

Так выглядит ошибка 502 в браузере
Мы воспроизвели три самые частые причины, и у каждой в журнале своя строка.
Приложение не запущено
Это самая простая и самая частая причина. В настройках Nginx сайт передаёт запросы приложению.
location / {
proxy_pass http://127.0.0.1:8098;
}Если приложение остановилось, упало или после перезагрузки сервера так и не запустилось, на его порту никого нет. Журнал при этом содержит уже знакомую строку.
2026/10/21 14:53:33 [error] 124894#124894: *3 connect() failed (111: Connection refused) while connecting to upstream, client: 127.0.0.1, server: example.com, request: "GET / HTTP/1.1", upstream: "http://127.0.0.1:8098/", host: "example.com"Код 111: Connection refused буквально означает, что соединение отвергнуто, потому что порт закрыт. В нашем примере причину показал журнал службы, который вы видели на скриншоте выше. Приложение не стартовало, потому что мы переименовали его файл настроек. Верните файл на место и запустите службу.
# Возвращаем файл настроек на место
mv /opt/errors-demo/config.json.old /opt/errors-demo/config.json
# Запускаем службу и проверяем ответ сайта
systemctl start errors-app
curl -I http://127.0.0.1:8091
Служба запущена, сайт снова отвечает кодом 200
Чтобы такая ошибка не повторялась после каждого сбоя, поручите systemd самому поднимать приложение. Для этого в файл службы добавьте две строки в раздел [Service].
[Service]
# Перезапускаем приложение, если оно упало
Restart=on-failure
# Ждём три секунды перед перезапуском
RestartSec=3Затем перечитайте настройки и включите автозапуск при загрузке сервера.
# Применяем изменения в файле службы
systemctl daemon-reload
# Включаем запуск вместе с сервером
systemctl enable errors-appПроверка получилась наглядной. Мы принудительно завершили процесс командой systemctl kill -s KILL errors-app. Сразу после этого сайт отвечал кодом 502, а через три секунды systemd запустил приложение заново и сайт снова открылся. Заметьте, что до включения автозапуска в строке Loaded на скриншоте стояло слово disabled. Оно означает, что после перезагрузки сервера служба сама не стартует.
PHP-FPM, сокет не найден
Для сайтов на PHP Nginx передаёт запросы не на порт, а в сокет, то есть специальный файл, через который он общается с PHP-FPM. Путь к нему указан в директиве fastcgi_pass.
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
}Если такого файла нет, Nginx отвечает 502, а журнал сообщает об этом открытым текстом.
2026/10/21 14:53:33 [crit] 124894#124894: *5 connect() to unix:/run/php/php8.1-fpm.sock failed (2: No such file or directory) while connecting to upstream, client: 127.0.0.1, server: example.com, request: "GET / HTTP/1.1", upstream: "fastcgi://unix:/run/php/php8.1-fpm.sock:", host: "example.com"Код 2: No such file or directory означает, что файла сокета не существует. Чаще всего причин две. Либо в настройках указано имя от другой версии PHP, например в инструкции для PHP 8.1, а у вас стоит 8.2, либо служба PHP-FPM остановлена, и её сокет исчез вместе с ней. Посмотрите, какие сокеты есть на сервере.
# Список сокетов PHP-FPM
ls /run/php/
В настройках указан php8.1-fpm.sock, а на сервере есть только php8.2-fpm.sock
В нашем примере в папке лежали php8.2-fpm.sock и php-fpm.sock. Исправьте имя в fastcgi_pass, проверьте настройки и примените их.
# Меняем версию в имени сокета
sed -i 's/php8.1-fpm.sock/php8.2-fpm.sock/' /etc/nginx/sites-available/example.com
# Проверяем настройки и применяем их
nginx -t && systemctl reload nginxФайл php-fpm.sock
В Debian рядом с сокетом конкретной версии лежит ещё php-fpm.sock. Это ссылка на сокет установленной версии PHP, и на нашем сервере она вела на php8.2-fpm.sock. Если указать в fastcgi_pass именно её, имя в настройках не придётся менять при смене версии PHP. Мы проверили, что сайт с таким путём отвечает нормально.
Если имя верное, а сокета всё равно нет, значит, остановился сам PHP-FPM. Мы остановили его командой systemctl stop php8.2-fpm и получили ту же ошибку 502 со строкой connect() to unix:/run/php/php8.2-fpm.sock failed (2: No such file or directory). Команда systemctl status php8.2-fpm показала inactive (dead), а после запуска службы сайт снова заработал.

PHP-FPM остановлен, сокета нет, после запуска службы сайт снова открывается
Если служба не запускается, причину покажет journalctl -u php8.2-fpm -n 50 --no-pager. А после правки настроек PHP-FPM проверяйте их командой php-fpm8.2 -t, как мы покажем дальше.
PHP-FPM, нет доступа к сокету
Бывает и так, что сокет на месте, но Nginx не может им пользоваться. В журнале тогда другой код.
2026/10/21 14:57:38 [crit] 133790#133790: *56 connect() to unix:/run/php/errors-fpm.sock failed (13: Permission denied) while connecting to upstream, client: 127.0.0.1, server: example.com, request: "HEAD / HTTP/1.1", upstream: "fastcgi://unix:/run/php/errors-fpm.sock:", host: "127.0.0.1:8099"
Сокет на месте, но принадлежит root, и Nginx не может к нему подключиться
Код 13: Permission denied означает, что у Nginx нет прав на этот файл. Nginx работает от имени пользователя www-data, а доступ к сокету определяют параметры listen.owner, listen.group и listen.mode в настройках пула PHP-FPM. По документации PHP, если не указать владельца, сокет получает пользователя, от которого запущена служба, а права 0660. В нашем примере мы создали отдельный пул errors в файле /etc/php/8.2/fpm/pool.d/errors.conf и не указали владельца.
[errors]
user = nobody
group = nogroup
listen = /run/php/errors-fpm.sock
pm = ondemand
pm.max_children = 2
pm.process_idle_timeout = 10sСлужба запускается от имени root, поэтому сокет получил владельца root и права srw-rw----, а www-data в эту группу не входит. Добавьте в пул две строки с владельцем.
# Переходим в папку с настройками пулов
cd /etc/php/8.2/fpm/pool.d
# Разрешаем пользователю Nginx работать с сокетом
printf 'listen.owner = www-data\nlisten.group = www-data\n' >> errors.conf
# Проверяем настройки и перезапускаем PHP-FPM
php-fpm8.2 -t && systemctl restart php8.2-fpm
# Смотрим владельца сокета и проверяем сайт
ls -l /run/php/errors-fpm.sock
curl http://127.0.0.1:8099/
Сокет принадлежит www-data, сайт отвечает
Нужен именно restart
Когда мы после правки сделали systemctl reload php8.2-fpm, владелец сокета остался прежним, root, и ошибка 502 не исчезла. Сокет сменил владельца только после systemctl restart php8.2-fpm.
Всегда проверяйте настройки перед перезагрузкой
Привыкайте запускать nginx -t перед каждой командой reload. Если в настройках ошибка, проверка её покажет, а работающий Nginx продолжит отвечать по старой конфигурации. Для PHP-FPM есть такая же проверка, команда php-fpm8.2 -t.
Ошибка 504 Gateway Time-out
Страница 504 говорит о том, что бэкенд жив и принял запрос, но слишком долго готовит ответ. Nginx ждёт его ограниченное время и, не дождавшись, сам прерывает ожидание. Время ожидания задаёт директива proxy_read_timeout, по умолчанию это 60 секунд, а для PHP-FPM такую же роль играет fastcgi_read_timeout. Важная деталь из документации Nginx в том, что это время отсчитывается между двумя последовательными чтениями, а не на весь ответ целиком. Если бэкенд присылает очередную порцию данных чаще, чем раз в 60 секунд, соединение не оборвётся.
Чтобы увидеть это своими глазами, мы добавили в тестовое приложение адрес /slow, который отвечает только через 75 секунд, как тяжёлый отчёт или долгий запрос к базе. Страница открывалась ровно минуту, а затем появилась ошибка.

Ошибка 504 появляется после минуты ожидания
Команда curl с параметром -w позволяет замерить время и увидеть код одним взглядом.
# Показываем код ответа и время ожидания
curl -s -o /dev/null -w '%{http_code} через %{time_total} с\n' http://127.0.0.1:8093/slowВ журнале при этом появилась такая запись.
2026/10/21 15:04:14 [error] 144934#144934: *81 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 127.0.0.1, server: example.com, request: "GET /slow HTTP/1.1", upstream: "http://127.0.0.1:8098/slow", host: "127.0.0.1:8093"
Код 504 через 60,06 секунды и строка upstream timed out в журнале
Слова while reading response header означают, что Nginx успешно подключился, отправил запрос и ждал начало ответа. Замер показал 60,06 секунды, то есть Nginx оборвал ожидание ровно по умолчанию.
Исправлять 504 лучше с причины. Выясните, что именно работает долго, например тяжёлый запрос к базе, обращение к чужому сервису или большой отчёт, и ускорьте его. Если задача по своей природе долгая, например выгрузка или импорт, увеличьте время ожидания для нужного адреса. Добавьте в блок location директиву proxy_read_timeout.
location / {
proxy_pass http://127.0.0.1:8098;
# Ждём ответ от приложения до двух минут
proxy_read_timeout 120s;
}Проверьте настройки и примените их, а затем повторите запрос.
# Проверяем настройки и применяем их
nginx -t && systemctl reload nginx
# Повторяем запрос
curl -s -o /dev/null -w '%{http_code} через %{time_total} с\n' http://127.0.0.1:8093/slow
Тот же запрос теперь возвращает 200 через 75 секунд
Для сайтов на PHP-FPM делается то же самое, только директива называется fastcgi_read_timeout и стоит в блоке location ~ \.php$ рядом с fastcgi_pass. Мы проверили это на скрипте, который отвечает через 70 секунд. С настройками по умолчанию он давал 504 ровно через 60 секунд, а со строкой fastcgi_read_timeout 120s; ответил кодом 200 через 70 секунд. Строка в журнале при этом выглядела так.
2026/10/21 15:07:46 [error] 153814#153814: *89 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 127.0.0.1, server: example.com, request: "GET /slow.php HTTP/1.1", upstream: "fastcgi://unix:/run/php/php8.2-fpm.sock", host: "127.0.0.1:8096"Таймаут Nginx не отменяет лимит PHP
У самого PHP есть собственный предел времени работы скрипта, параметр max_execution_time. В файле /etc/php/8.2/fpm/php.ini на нашем сервере он равен 30 секундам. Мы запустили скрипт, который непрерывно занимал процессор, и PHP остановил его ровно на 30 секундах. В ответ пришла не 504, а ошибка 500, а в журнале появилась строка PHP Fatal error: Maximum execution time of 30 seconds exceeded in /var/www/errors-demo-php/busy.php on line 3. Скрипт, который просто ждал вызовом sleep(70), под это ограничение не попал, потому что по документации PHP время ожидания системных вызовов на Linux не учитывается. Если ваш скрипт упирается именно в лимит PHP, поднимайте max_execution_time, а не только таймаут Nginx.
Ошибка 403 Forbidden
Страница 403 означает, что Nginx запрос понял, но отказался на него отвечать. На практике причин две. Либо в папке нечего показывать, либо у Nginx нет прав прочитать файл. В журнале эти случаи легко различить, поэтому всегда начинайте с него.

Так выглядит ошибка 403 в браузере
Нет индексного файла
Когда посетитель открывает адрес папки, например https://example.com/, Nginx ищет в ней главную страницу. Имя файла задаёт директива index, по умолчанию это index.html. Если такого файла нет, Nginx мог бы показать список файлов папки, но по умолчанию это выключено (autoindex off), и поэтому он отвечает 403.
2026/10/21 14:53:33 [error] 124894#124894: *7 directory index of "/var/www/errors-demo-403a/" is forbidden, client: 127.0.0.1, server: example.com, request: "GET / HTTP/1.1", host: "example.com"Фраза directory index of … is forbidden как раз и означает, что в папке нет индексного файла, а список файлов запрещён. Посмотрите, что лежит в папке сайта.
# Смотрим содержимое папки сайта
ls -la /var/www/errors-demo-403a
В папке сайта нет главной страницы, поэтому Nginx отвечает 403
Папка оказалась пустой. Чаще всего так случается, когда сайт загрузили не в ту папку или забыли загрузить сам файл главной страницы. Проверьте, что директива root указывает на папку, где реально лежит сайт, и что в ней есть файл с именем из директивы index. Если для PHP-сайта это index.php, убедитесь, что оно указано в index index.html index.php;. После загрузки файла ответ меняется сразу, перезагружать Nginx не нужно.
# Создаём главную страницу
echo '<h1>Сайт работает</h1>' > /var/www/errors-demo-403a/index.html
# Проверяем ответ
curl -sI http://127.0.0.1:8094/ | head -1После этого сервер ответил HTTP/1.1 200 OK.
Если вам нужен именно список файлов, например для папки с архивами, включите его директивой autoindex on; в нужном блоке location. Мы проверили, что Nginx в этом случае отдаёт страницу Index of / со списком файлов папки.
Список файлов виден всем
При autoindex on любой посетитель увидит имена всех файлов в папке и сможет скачать каждый из них. Включайте его только для папок, которые вы готовы показать целиком.
Нет прав на файл
Во втором случае файл на месте, но Nginx не может его прочитать. Журнал сообщает об этом так.
2026/10/21 14:53:33 [error] 124894#124894: *8 open() "/var/www/errors-demo-403b/index.html" failed (13: Permission denied), client: 127.0.0.1, server: example.com, request: "GET / HTTP/1.1", host: "example.com"Код 13: Permission denied означает отказ в доступе на уровне Linux. Чтобы понять, чьи права важны, узнайте, от имени кого работает Nginx, а затем посмотрите права на каждый элемент пути командой namei.
# От имени какого пользователя работает Nginx
grep ^user /etc/nginx/nginx.conf
# Права на каждую папку и файл на пути
namei -l /var/www/errors-demo-403b/index.html
Nginx работает от www-data, а файл закрыт для всех, кроме root
Nginx работает от имени www-data, а файл имеет права -rw------- и принадлежит root, то есть читать его может только владелец. Для www-data это посторонний пользователь, и доступа у него нет. Такую картину мы получили, создав файл от имени root с правами 600. Для сайта подходит простое правило. Папкам ставят 755, файлам 644, а владельцем делают пользователя веб-сервера. Как устроены права и что означают эти цифры, рассказано в статье про права доступа в Linux.
# Передаём сайт пользователю веб-сервера
chown -R www-data:www-data /var/www/errors-demo-403b
# Папкам 755, файлам 644
find /var/www/errors-demo-403b -type d -exec chmod 755 {} +
find /var/www/errors-demo-403b -type f -exec chmod 644 {} +
Файл доступен для чтения, сайт отвечает кодом 200
Для чтения файла Nginx достаточно права r для «остальных». Мы проверили, что при владельце root и правах 644 сайт тоже открывается, а смена владельца нужна, чтобы сайт мог сам сохранять загружаемые файлы.
Не лечите 403 правами 777
Права 777 разрешают всем пользователям сервера читать и изменять файл, и ошибка действительно пропадает. Но вместе с ней пропадает и защита сайта, поэтому такой совет из интернета лучше не применять. Задайте владельцем пользователя www-data и выставьте 755 и 644.
Нет прав на папку на пути к файлу
Иногда сам файл доступен всем, а ошибка всё равно остаётся. Чтобы добраться до файла, Nginx должен пройти через каждую папку на пути, и для этого у www-data на каждой из них нужно право x. Достаточно, чтобы одна папка оказалась закрыта. Журнал в этом случае выглядит немного иначе, в нём нет слова open().
2026/10/21 15:08:10 [error] 156517#156517: *109 "/var/www/errors-demo-403c/public/index.html" is forbidden (13: Permission denied), client: 127.0.0.1, server: example.com, request: "GET / HTTP/1.1", host: "127.0.0.1:8095"В нашем примере файл index.html имеет права 644, но папка выше него закрыта. Найти её поможет всё та же команда namei.
# Ищем закрытую папку на пути к файлу
namei -l /var/www/errors-demo-403c/public/index.html
Папка errors-demo-403c закрыта для всех, кроме root, после chmod 755 сайт открывается
В выводе у папки errors-demo-403c стоят права drwx------, остальные папки открыты. Откройте её для чтения и входа.
# Разрешаем входить в папку всем пользователям
chmod 755 /var/www/errors-demo-403cТакая картина типична для сайтов, которые лежат в закрытой папке, например в домашней. На нашем сервере у папки /root права 700, поэтому выкладывать сайт в неё не стоит. Для сайтов используйте /var/www.
Ошибка 500 Internal Server Error
Код 500 называют самым неопределённым, потому что он означает просто «что-то пошло не так при обработке запроса». Поэтому здесь особенно важно заглянуть в журнал. Мы воспроизвели две причины, которые встречаются чаще других. Первая относится к PHP, вторая к самому Nginx.
Фатальная ошибка в PHP
Если PHP-скрипт упал, PHP-FPM отвечает кодом 500. По умолчанию PHP не показывает посетителю текст ошибки, и в нашем случае параметр display_errors в /etc/php/8.2/fpm/php.ini был выключен, а log_errors включён. Тело ответа оказалось пустым, поэтому надпись нарисовал сам браузер, а не Nginx.

При пустом ответе браузер показывает собственную страницу с кодом HTTP ERROR 500
Зато всё, что хотел сказать PHP, попадает в журнал Nginx.
2026/10/21 15:09:26 [error] 156517#156517: *123 FastCGI sent in stderr: "PHP message: PHP Fatal error: Uncaught Error: Call to undefined function mb_strlen() in /var/www/errors-demo-php/page.php:3
Stack trace:
#0 {main}
thrown in /var/www/errors-demo-php/page.php on line 3" while reading response header from upstream, client: 127.0.0.1, server: example.com, request: "GET /page.php HTTP/1.1", upstream: "fastcgi://unix:/run/php/php8.2-fpm.sock:", host: "127.0.0.1:8096"
Код 500 и строка из журнала с текстом ошибки PHP
Запись начинается словами FastCGI sent in stderr, а после них идёт само сообщение PHP. Читать его нужно так. Тип ошибки PHP Fatal error, затем суть, Call to undefined function mb_strlen(), то есть вызвана функция, которой нет, и в конце файл и строка, где это произошло. В нашем примере функция mb_strlen() относится к модулю mbstring, который на свежей установке PHP отсутствует. Этот модуль входит в список, который мы советуем поставить для WordPress в статье про установку Nginx. Установите недостающий модуль.
# Устанавливаем модуль mbstring для PHP
apt install -y php-mbstringПосле установки мы ничего не перезапускали, и страница сразу заработала.

Модуль установлен, страница возвращает 200 и результат работы функции
Другая частая причина 500 в PHP, синтаксическая ошибка после правки файла. Проверить файл на сервере можно командой php -l, и она же скажет, в какой строке ошибка.
# Проверяем синтаксис файла
php -l /var/www/errors-demo-php/parse.phpPHP Parse error: syntax error, unexpected token "echo", expecting "," or ";" in parse.php on line 3
Errors parsing parse.phpНе включайте показ ошибок на рабочем сайте
Параметр display_errors можно включить, чтобы увидеть ошибку прямо в браузере, но на рабочем сайте так делать не стоит. Текст ошибки содержит пути к файлам на сервере, и его увидит любой посетитель. Журнал Nginx даёт ту же информацию, но только вам.
Цикл внутренних перенаправлений
Вторая причина не связана с приложением вовсе, это ошибка в настройках самого Nginx. Рассмотрим настройку, при которой для любого адреса, где нет подходящего файла, отдаётся главная страница /index.html.
root /var/www/errors-demo-cycle;
location / {
try_files $uri /index.html;
}Последний параметр try_files работает как внутреннее перенаправление. Nginx начинает обработку заново уже для адреса /index.html и снова попадает в тот же location. Если файл index.html существует, всё заканчивается успешно. Если же его нет, например сборку сайта забыли загрузить или root указывает не туда, Nginx снова перенаправляет запрос на /index.html, и так по кругу. По документации Nginx такой цикл прерывается после десяти попыток, и клиент получает ошибку 500.

Здесь ошибку 500 нарисовал сам Nginx, поэтому внизу видна его версия
В журнале запись говорит об этом прямо.
2026/10/21 14:53:33 [error] 124894#124894: *11 rewrite or internal redirection cycle while internally redirecting to "/index.html", client: 127.0.0.1, server: example.com, request: "GET / HTTP/1.1", host: "example.com"Фраза rewrite or internal redirection cycle означает циклическое перенаправление, а в кавычках стоит адрес, на который Nginx ходил по кругу. Проверьте, есть ли такой файл в папке, указанной в root, и при необходимости загрузите его.
# Смотрим, что лежит в папке сайта
ls -la /var/www/errors-demo-cycle
# Кладём файл на место
echo '<h1>Сайт работает</h1>' > /var/www/errors-demo-cycle/index.html
# Проверяем ответ
curl -sI http://127.0.0.1:8097/ | head -1
Цикл прекращается, как только нужный файл появляется в папке сайта
После создания файла ответ стал HTTP/1.1 200 OK. Перезагружать Nginx для этого не потребовалось, ведь исправлять пришлось не настройки, а содержимое папки.
Частые вопросы
upstream prematurely closed connection while reading response header from upstream
Эта строка в журнале сопровождает ошибку 502. Nginx подключился к приложению и отправил запрос, но приложение оборвало соединение, не прислав ответ. Например, оно могло упасть прямо во время обработки запроса. Посмотрите журнал службы командой journalctl -u имя-службы -n 50 --no-pager, там должна быть причина. Мы воспроизвели это строкой вида upstream prematurely closed connection while reading response header from upstream, … request: "GET /close HTTP/1.1", upstream: "http://127.0.0.1:8098/close".
upstream sent too big header while reading response header from upstream
Ошибка 502 появляется, когда приложение присылает слишком длинные заголовки ответа, например огромные cookie. Nginx читает первую часть ответа в буфер, размер которого по умолчанию равен одной странице памяти, то есть 4 или 8 КБ. Если заголовки в него не помещаются, ответ считается недопустимым. Мы проверили это на заголовке длиной 9000 символов. Увеличьте буфер в блоке location.
location / {
proxy_pass http://127.0.0.1:8098;
# Буфер для заголовков ответа побольше
proxy_buffer_size 16k;
proxy_buffers 8 16k;
}Строку proxy_buffers 8 16k; пропускать нельзя. Когда мы добавили только proxy_buffer_size 16k;, nginx -t сообщил об ошибке "proxy_busy_buffers_size" must be less than the size of all "proxy_buffers" minus one buffer, и применять такие настройки нельзя. С обеими строками проверка прошла, а страница открылась с кодом 200.
nginx -t пишет [emerg] после правки настроек
Это значит, что в файле настроек ошибка, и Nginx не станет применять такую конфигурацию. Пока вы не выполните systemctl reload nginx, сайт продолжает работать по старым настройкам, поэтому всегда запускайте nginx -t && systemctl reload nginx, а не одну команду reload. Текст сообщения указывает, что именно не так. Например, "proxy_busy_buffers_size" must be less than the size of all "proxy_buffers" minus one buffer говорит о несогласованных размерах буферов в блоке location.
Сайт показывает ошибку, а в error.log пусто
Скорее всего, у этого сайта свой журнал ошибок. Он задаётся директивой error_log в блоке server, и тогда записи попадают в указанный файл, а не в общий error.log. Мы проверили это. С директивой error_log /var/log/nginx/errors-demo.log; в блоке сайта строка про ошибку 403 появилась только в этом файле. Найти все журналы, которые знает Nginx, можно командой ниже.
# Показываем все директивы error_log во всех настройках
nginx -T 2>/dev/null | grep -n error_logВ нашем примере команда вывела error_log /var/log/nginx/error.log; из основного файла и строку с адресом журнала сайта.
curl: (7) Failed to connect to … port … Couldn't connect to server
Это уже не одна из четырёх ошибок. Nginx не прислал вообще никакого ответа, потому что на этом порту никто не слушает. Браузер в таком случае показывает собственную страницу, у Chrome это ERR_CONNECTION_REFUSED. Мы получили такое сообщение при обращении к порту, который никто не занимал, curl: (7) Failed to connect to 127.0.0.1 port 8090 after 0 ms: Couldn't connect to server. Проверьте, что сам Nginx запущен, командой systemctl status nginx, и что порты 80 и 443 открыты в брандмауэре.
PHP-FPM в журнале пишет Maximum execution time of 30 seconds exceeded
Скрипт работал дольше, чем разрешает параметр max_execution_time, по умолчанию 30 секунд, и PHP его остановил. Nginx при этом возвращает 500, а не 504. Откройте /etc/php/8.2/fpm/php.ini, увеличьте значение параметра и перезапустите службу командой systemctl restart php8.2-fpm. Если скрипт должен работать дольше минуты, не забудьте поднять и fastcgi_read_timeout в настройках Nginx.
Наши ресурсы
Telegram-канал: https://t.me/f3cloud_news
Официальный сайт: https://f3cloud.com