Разбор четырёх частых ошибок nginx в одном месте: что значит код, 2-4 типовые причины, команда для проверки и точное исправление. С реальными строками из error.log.

Ошибки nginx 404, 502, 504 и 413 выглядят пугающе, но за каждой стоит короткое сообщение о конкретной поломке, и почти всегда оно уже лежит в логе. Разберём все четыре по одной схеме: что означает код, какие 2-4 причины стоят за ним на обычном сервере, какой командой причину подтвердить и что поправить. Примеры - для Ubuntu 24.04 (там php-fpm 8.3) и Debian; на других версиях подставьте свою: служба phpX.Y-fpm, пути /etc/php/X.Y/fpm/…, socket /run/php/phpX.Y-fpm.sock. По умолчанию PHP: 8.1 на Ubuntu 22.04, 8.2 на Debian 12, 8.4 на Debian 13, 8.5 на Ubuntu 26.04.

Коротко. 404 - nginx не нашёл файл по пути из root / alias либо нет try_files; в логе open() ... failed (2: No such file or directory). 502 - upstream (php-fpm, приложение) не слушает или указан не тот socket; в логе connect() failed (111: Connection refused). 504 - upstream не ответил за отведённое время (по умолчанию 60 секунд); в логе upstream timed out (110: Connection timed out). 413 - тело запроса больше client_max_body_size (по умолчанию 1m); в логе client intended to send too large body. Логи: /var/log/nginx/error.log и /var/log/nginx/access.log, смотреть через sudo tail -f.

Как читать логи nginx

У nginx два основных лога, и они про разное. access_log - строка на каждый запрос: кто пришёл, что запросил, какой код получил. error_log - запись только когда что-то сломалось: путь к файлу, системная ошибка в скобках, адрес клиента. Для 404, 502, 504 и 413 нужен прежде всего error_logaccess_log помогает увидеть, дошёл ли запрос и с каким кодом ушёл ответ.

Самый быстрый способ - следить за логом в реальном времени и повторить проблемный запрос в браузере.

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

  • -f - дописывать новые строки, пока не нажмёте Ctrl+C. Держите в отдельном окне терминала: нужная строка появится сразу.

Уровни error_log от подробного к редкому: debuginfonoticewarnerrorcritalertemerg; по умолчанию - error и выше, для наших ошибок хватает. Если лог пуст, а проблема есть, чаще всего у сайта в его server-блоке задан отдельный файл. Проверить, какие логи в деле:

sudo nginx -T | grep -nE 'access_log|error_log'

  • nginx -T печатает итоговую конфигурацию со всеми подключёнными файлами, а не только nginx.confgrep -n добавит номер строки и путь.

Что значат слова reverse proxy, upstream, FastCGI и socket

  • reverse proxy (обратный proxy) - сервер, который принимает запрос из интернета, передаёт его внутреннему приложению и возвращает клиенту ответ. nginx на сайте почти всегда работает так.
  • upstream - то внутреннее приложение, которому nginx передаёт запрос: php-fpm, процесс на Node.js или Python, другой веб-сервер.
  • FastCGI - протокол, по которому nginx общается с php-fpm. Это не HTTP, поэтому curl-ом php-fpm напрямую не проверить.
  • socket - точка, куда nginx стучится к upstream: сетевой (адрес и порт, 127.0.0.1:9000) или Unix-socket - файл вида /run/php/php8.3-fpm.sock.

Ошибки nginx 404, 502, 504 и 413: таблица быстрой диагностики

Сначала общая карта. Ниже каждая ошибка разобрана подробно.

Симптом

Скорее всего

Первая команда

Что делать

404 на статике или на всех страницах, кроме главной

Неверный root / alias или нет try_files

sudo tail -f /var/log/nginx/error.log и повторить запрос

Указать настоящий каталог; добавить try_files в location

404 на всех доменах или отдаётся чужой сайт

Запрос ловит сервер по умолчанию

sudo nginx -T | grep -nE 'listen|server_name'

Проверить server_name и какой блок сейчас default

403 вместо 404, файл на месте

Нет прав на чтение файла или на проход по каталогу пути

namei -l /var/www/site/public/index.php

Поправить владельца/группу или права именно на проблемном участке пути; не 777

502 сразу, без задержки

php-fpm или приложение не слушает либо указан не тот socket

systemctl status php8.3-fpmsudo ss -xlnp | grep -i php

Запустить сервис; выровнять fastcgi_pass с listen пула

502 под нагрузкой, в логе no live upstreams

Все серверы upstream-группы временно помечены недоступными (после предыдущих ошибок подключения)

journalctl -u php8.3-fpm -n 50

Смотреть более ранние ошибки подключения/таймауты; проверить max_fails / fail_timeout

504 примерно через 60 секунд

Upstream не передавал данные в течение read timeout

grep 'upstream timed out' /var/log/nginx/error.log

Найти медленный запрос через slowlog; как заплатку - поднять *_read_timeout

413 при загрузке файла или большом POST

client_max_body_size (по умолчанию 1m)

grep 'too large body' /var/log/nginx/error.log

Поднять client_max_body_size и согласовать с upload_max_filesize / post_max_size, затем reload

404 Not Found: nginx не нашёл файл

Одна из самых частых причин nginx-404 - файл не найден по пути, вычисленному из root (корень сайта) плюс URL запроса либо из alias для конкретного location. Реже 404 отдаёт сам конфиг намеренно - через return 404; или как последний аргумент try_files. Быстрый признак, что ответил именно nginx, а не приложение: голая страница 404 Not Found с подписью nginx в подвале. Это не стопроцентное доказательство - можно настроить свою error_page или скрыть server_tokens, - но в большинстве случаев работает.

Четыре типовые причины. Неверный root или alias - путь указывает не туда, где лежат файлы. Нет try_files - для WordPress с человекопонятными адресами и одностраничных приложений (SPA) запрос вида /about не совпадает с файлом на диске, и без try_files nginx не передаёт его на index.php или index.htmlЗапрос ловит чужой server-блок - ни один server_name не совпал с доменом, и nginx отдал запрос серверу по умолчанию, где другой root. Отдельно: файл на месте, но у пользователя nginx (обычно www-dataнет прав прочитать его или пройти по каталогам пути - тогда код 403, а не 404, но разбирают это вместе.

Подтверждаем по логу. В соседнем терминале держим tail -f на error.log и повторяем запрос - строка выглядит примерно так:

2026/09/03 12:14:02 [error] 5123#5123: *7 open() "/var/www/example.com/public/about"
failed (2: No such file or directory), client: 203.0.113.10, server: example.com,
request: "GET /about HTTP/1.1", host: "example.com"

  • (2: No such file or directory) - файла по этому пути нет; сравните путь в кавычках с тем, где файлы лежат на самом деле.
  • (13: Permission denied) в такой же строке - файл есть, но нет прав; это про 403.
  • directory index of "/var/www/example.com/public/" is forbidden - запрос пришёл на каталог, индексного файла нет, автолистинг выключен; тоже 403.

Смотрим, какой root и try_files реально применяются и не перехватывает ли домен чужой блок:

sudo nginx -T | grep -nE 'root|alias|try_files|server_name'

Проверяем, что путь читается пользователем nginx и на всех каталогах выше есть право на проход:

namei -l /var/www/example.com/public/index.php

  • namei -l разворачивает путь по каталогам и показывает права на каждом уровне. Пользователю nginx (обычно www-data) нужны r на файле и x на каждом каталоге выше по пути - доступ может идти через владельца, группу или права "для остальных", любой из трёх подходит. Нет x хотя бы на одном уровне ни по одному из трёх - вот причина 403.

Исправление. Неверный root - прописываем настоящий каталог с файлами (где лежит index.php или index.html), затем nginx -t и reload:

sudo nginx -t && sudo systemctl reload nginx

Нет try_files - добавляем в location. Для WordPress:

location / {
try_files $uri $uri/ /index.php?$args;
}

Для SPA на React или Vue - с отдачей index.html:

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

  • try_files по очереди проверяет: есть ли такой файл ($uri), есть ли каталог ($uri/), и если нет - отдаёт последний аргумент. Так /about попадает в index.php или index.html, а адрес разбирает само приложение.

Запрос ловит чужой блок - проверяем server_name в нужном server-блоке и то, какой блок сейчас помечен default_server. Обычно достаточно поправить server_name; специально делать нужный сайт default-сервером ради перехвата ошибочных запросов по Host не стоит - для этого заводят отдельный catch-all:

server {
listen 80 default_server;
server_name _;
return 404;
}

Нет прав - для простого статического webroot часто достаточно:

sudo find /var/www/example.com -type d -exec chmod 755 {} \;
sudo find /var/www/example.com -type f -exec chmod 644 {} \;

Это не универсальная команда для любого сайта: она меняет права всего дерева целиком, включая конфиги с чувствительными данными, каталоги, куда приложение должно писать, и файлы с особыми правами. Точнее - поправить владельца, группу или права именно на проблемном участке пути, который показал namei -l, и не трогать остальное. Без 777 в любом случае.

502 Bad Gateway: upstream не принимает соединение

502 означает, что nginx как обратный proxy попытался передать запрос upstream-у (php-fpm, приложению на Node.js или Python, другому серверу) и не смог установить соединение либо оно оборвалось на полуслове. Обычно приходит сразу.

Причины: upstream не запущен или упалв fastcgi_pass или proxy_pass указан не тот socket или портприложение слушает, но AppArmor или SELinux не пускает nginx к сокетуприложение захлебнулось нагрузкой и отклоняет подключения - в логе тогда no live upstreams (когда в блоке upstream несколько серверов и все помечены недоступными).

Строки лога, по которым различают причину:

connect() failed (111: Connection refused) while connecting to upstream, client: 203.0.113.10,
server: example.com, request: "POST /index.php HTTP/1.1",
upstream: "fastcgi://127.0.0.1:9000", host: "example.com"

  • (111: Connection refused) - по адресу никто не слушает: сервис не запущен либо слушает другой адрес.
  • connect() to unix:/run/php/php8.3-fpm.sock failed (2: No such file or directory) - файла сокета нет, php-fpm не поднялся.
  • connect() to unix:/run/php/php8.3-fpm.sock failed (13: Permission denied) - файл сокета на месте, но у nginx нет прав к нему подключиться; смотрите listen.owner / listen.group / listen.mode ниже.
  • recv() failed (104: Connection reset by peer) while reading response header from upstream - соединение было, но upstream его оборвал; обычно упал воркер PHP (нехватка памяти или фатальная ошибка).
  • no live upstreams while connecting to upstream - все серверы группы upstream признаны недоступными.

Проверяем сам сервис:

systemctl status php8.3-fpm

  • В норме - active (running)failed или inactive (dead) - причина найдена; подробности в journalctl -u php8.3-fpm -n 50.

Смотрим, на каком сокете php-fpm реально слушает, и сверяем с fastcgi_pass:

sudo ss -xlnp | grep -i php
sudo nginx -T | grep -nE 'fastcgi_pass|proxy_pass'

  • ss -xlnp - список слушающих Unix-сокетов с процессом: -l слушающие, -x Unix, -n без имён, -p процесс. Путь в выводе (/run/php/php8.3-fpm.sock) должен совпадать с fastcgi_pass. Для upstream по TCP - ss -ltnp | grep 3000.

Если upstream - обычное HTTP-приложение за proxy_pass, стучимся к нему напрямую, в обход nginx:

curl -i http://127.0.0.1:3000/

  • Успешный ответ (код зависит от приложения) - upstream жив, проблема между ним и nginx. Connection refused - приложение не слушает этот адрес.
  • php-fpm так не проверить: он говорит по FastCGI, а не по HTTP.

Если сокет на месте, php-fpm работает, а 502 остаётся, - частая причина: у пользователя nginx (www-data) просто нет прав подключиться к Unix-сокету. В логе тогда (13: Permission denied) вместо (111: Connection refused). Проверяем владельца, группу и режим файла сокета и сравниваем с тем, что задано в пуле php-fpm:

ls -l /run/php/php8.3-fpm.sock
grep -E '^listen\.(owner|group|mode)' /etc/php/8.3/fpm/pool.d/www.conf

  • Для связки nginx/php-fpm доступ к сокету обычно настраивают через listen.ownerlisten.group и listen.mode в конфиге пула - именно они, а не защита ядра, первый подозреваемый при Permission denied.

Если с сокетом всё в порядке, а 502 всё равно есть, - смотрим на защиту ядра, это уже второй эшелон диагностики. На Ubuntu и Debian это AppArmor - ищем отказы в sudo journalctl -k | grep -iE 'apparmor.*denied' (или в /var/log/audit/audit.log, если установлен auditd); на системах с SELinux (AlmaLinux и родственные) - getenforce, затем sudo ausearch -m avc -ts recent и, если нужно, sudo setsebool -P httpd_can_network_connect 1.

Исправление. Upstream упал - поднимаем и разбираемся, почему:

sudo systemctl restart php8.3-fpm

Не тот socket - приводим fastcgi_pass к значению listen из пула php-fpm (/etc/php/8.3/fpm/pool.d/www.conf, строка listen =), затем nginx -t и reload. Захлёбывается под нагрузкой - смотрим pm.max_children в www.conf; строка в логе php-fpm server reached pm.max_children setting (5), consider raising it прямо просит поднять лимит. Но сначала проверьте память: каждый воркер PHP - это десятки мегабайт, и если сервер упирается в память или CPU, лишние воркеры не помогут - дело может быть в тарифе.

504 Gateway Timeout: upstream не уложился в таймаут

504 Gateway Timeout означает, что nginx не дождался upstream-а на одном из этапов: либо не смог за отведённое время подключиться к нему, либо подключился, но не дождался ответа. По умолчанию отведённое время - 60 секунд: fastcgi_read_timeout и proxy_read_timeout оба равны 60s. Важный нюанс: это не максимальное время на весь запрос целиком, а таймаут между двумя последовательными операциями чтения ответа. Если upstream время от времени присылает данные (например, потоковый ответ), запрос технически может идти намного дольше 60 секунд и 504 не случится - так прямо написано в документации nginx.

Причины: сама страница медленная (тяжёлый отчёт, экспорт, генерация файла); за приложением медленный запрос к базе или вызов внешнего API без своего таймаута; реже - таймаут в nginx занижен; ещё реже upstream не успевает даже принять соединение (перегрузка, в логе while connecting to upstream).

grep 'upstream timed out' /var/log/nginx/error.log | tail

upstream timed out (110: Connection timed out) while reading response header from upstream,
client: 203.0.113.10, server: example.com, request: "GET /export?range=year HTTP/1.1",
upstream: "fastcgi://unix:/run/php/php8.3-fpm.sock", host: "example.com"

  • while reading response header - nginx дождался соединения, но ответа нет: проблема внутри приложения. Тот же (110), но while connecting to upstream - не успел даже подключиться: перегрузка upstream-а или сети.
  • В поле request: виден URL, который тормозит, - с него и начинаем.

Ловим медленные запросы PHP через slowlog. В /etc/php/8.3/fpm/pool.d/www.conf:

slowlog = /var/log/php8.3-fpm.slow.log
request_slowlog_timeout = 5s

Дальше sudo systemctl reload php8.3-fpm, воспроизводим страницу и читаем лог - там стек PHP, зависший на конкретной функции: запрос к базе, curl к внешнему API.

Исправление. Настоящее решение - ускорить саму операцию: индекс на запрос к базе, постраничная выдача вместо выгрузки всего сразу, тяжёлая работа в фоновой очереди, таймаут на внешние вызовы. Временно, пока идёт починка, поднимаем таймаут в нужном location - именно там, не глобально:

location ~ \.php$ {
fastcgi_read_timeout 120s;
}

Для proxy_pass - proxy_read_timeout 120s;. Дальше nginx -t и reload. Запрос всё равно будет висеть две минуты вместо одной - это заплатка, а не лечение.

413 Request Entity Too Large: запрос больше лимита

413 означает, что тело запроса - загружаемый файл или крупный POST - превысило client_max_body_size. По умолчанию это 1m, то есть первый же файл тяжелее мегабайта упрётся в стену. Браузер эту ошибку часто показывает криво или просто обрывает загрузку.

grep 'too large body' /var/log/nginx/error.log | tail

client intended to send too large body: 8388608 bytes, client: 203.0.113.10,
server: example.com, request: "POST /wp-admin/async-upload.php HTTP/1.1", host: "example.com"

  • Число в байтах - сколько клиент пытался отправить. 8388608 - это 8 МБ; лимит ставим с запасом над этим значением.

Исправление. Поднимаем лимит в nginx. Директиву можно поставить в http (на весь сервер), server (на один сайт) или location (только на путь загрузки); значение из более узкого контекста перекрывает более широкий.

server {
client_max_body_size 50m;
}

Затем sudo nginx -t && sudo systemctl reload nginx - именно reload, restart не нужен.

Одного nginx мало: у PHP свои лимиты, и без них стена просто переедет на слой ниже. В /etc/php/8.3/fpm/php.ini, если хотите принимать файлы до 50 МБ:

upload_max_filesize = 50M
post_max_size = 55M

  • upload_max_filesize - максимум на один файл; post_max_size - на всё тело POST целиком, и по документации PHP должен быть больше (не просто не меньше) upload_max_filesize - тело POST помимо файла несёт ещё multipart-обвязку и остальные поля формы. Возьмите его с запасом, например на 5-10% выше. Формального требования «post_max_size ≥ client_max_body_size» нет - важно просто согласовать оба значения с тем реальным максимумом загрузки, который вы хотите разрешить. После правки - sudo systemctl reload php8.3-fpm.

Если за nginx стоит не PHP, а приложение на Node.js или Python, у его framework свой предел на размер тела - его тоже придётся поднять.

FAQ

Как понять, что 404 отдаёт именно nginx, а не WordPress или framework?

Посмотрите на страницу. Голый текст 404 Not Found с подписью nginx в подвале - это nginx, запрос не дошёл до приложения. Оформленная страница с шапкой сайта - 404 вернуло само приложение. В логе nginx первый случай - строка open() ... failed (2: No such file or directory).

502 или 504 - в чём разница?

502 - nginx не смог передать запрос upstream-у: соединение отклонено, оборвано или сервис не слушает; приходит обычно сразу. 504 - nginx подключился (или пытался подключиться) к upstream-у, но не дождался его на этом этапе за отведённый таймаут (по умолчанию 60 секунд). В логе это connect() failed против upstream timed out.

Куда писать client_max_body_size - в httpserver или location?

В любой из трёх. В http - на весь сервер, в server - на один сайт, в location - только на конкретный путь вроде формы загрузки. Значение из более узкого контекста перекрывает более широкий, так что общий лимит можно держать небольшим и поднимать точечно.

Поднял client_max_body_size, а 413 осталась?

Лимит переехал на слой PHP. Поднимите upload_max_filesize и post_max_size в php.ini, при этом post_max_size должен быть больше upload_max_filesize (тело POST шире одного файла), и выполните sudo systemctl reload php8.3-fpm. Для приложения на Node.js или Python проверьте лимит в его framework.

Помогает ли при 504 просто увеличить таймаут?

Как временная мера - да, страница перестанет отдавать 504. Но запрос всё равно будет выполняться долго, и пользователь будет ждать. Параллельно ищите медленный запрос через slowlog и убирайте причину: индекс в базе, постраничная выдача, фоновая очередь.

После правки конфига нужно перезапускать nginx?

Нет, достаточно перезагрузки: sudo nginx -t && sudo systemctl reload nginx. Она применяет новый конфиг без разрыва текущих соединений. Параметры PHP применяются командой sudo systemctl reload php8.3-fpm.

Логи пустые, а ошибка есть - почему?

Скорее всего смотрите не тот файл: у сайта в его server-блоке может быть свой error_log и access_log. Реже - уровень error_log выше события. Проверьте пути: sudo nginx -T | grep -nE 'error_log|access_log'.

Может ли 502 или 504 быть из-за нехватки ресурсов сервера?

Да. Если приложению не хватает памяти и его снимает OOM-killer (механизм ядра Linux, убивающий процесс при нехватке памяти), nginx увидит отказ соединения - 502. Если перегружен процессор и ответы идут медленно - 504. Тогда конфиг nginx ни при чём, и если сервер не тянет нагрузку, дело может быть в тарифе.

Коротко

  • Все четыре ошибки читаются в /var/log/nginx/error.logsudo tail -f и повторить запрос.
  • 404 - open() ... failed (2: No such file or directory): неверный root / alias или нет try_files. Права на файл и проход по каталогам дают 403 - смотрите namei -l.
  • 502 - connect() failed (111: Connection refused): upstream не слушает или не тот socket. Смотрите systemctl status php8.3-fpm и ss -xlnp.
  • 504 - upstream timed out (110: Connection timed out): upstream не передавал данные в течение read timeout (по умолчанию 60 секунд между чтениями, не на весь запрос). Ищите медленный запрос через slowlog; поднятие таймаута - временно.
  • 413 - тело больше client_max_body_size (1m). Поднимайте её вместе с upload_max_filesize и post_max_size, затем reload обоих сервисов.
  • Конфиг nginx применяется через nginx -t && systemctl reload nginx, без перезапуска.