Разбор четырёх частых ошибок 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_log; access_log помогает увидеть, дошёл ли запрос и с каким кодом ушёл ответ.
Самый быстрый способ - следить за логом в реальном времени и повторить проблемный запрос в браузере.
sudo tail -f /var/log/nginx/error.log
-f- дописывать новые строки, пока не нажмёте Ctrl+C. Держите в отдельном окне терминала: нужная строка появится сразу.
Уровни error_log от подробного к редкому: debug, info, notice, warn, error, crit, alert, emerg; по умолчанию - error и выше, для наших ошибок хватает. Если лог пуст, а проблема есть, чаще всего у сайта в его server-блоке задан отдельный файл. Проверить, какие логи в деле:
sudo nginx -T | grep -nE 'access_log|error_log'
nginx -Tпечатает итоговую конфигурацию со всеми подключёнными файлами, а не толькоnginx.conf;grep -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 на статике или на всех страницах, кроме главной | Неверный |
| Указать настоящий каталог; добавить |
404 на всех доменах или отдаётся чужой сайт | Запрос ловит сервер по умолчанию | | Проверить |
403 вместо 404, файл на месте | Нет прав на чтение файла или на проход по каталогу пути | | Поправить владельца/группу или права именно на проблемном участке пути; не 777 |
502 сразу, без задержки | php-fpm или приложение не слушает либо указан не тот socket |
| Запустить сервис; выровнять |
502 под нагрузкой, в логе | Все серверы upstream-группы временно помечены недоступными (после предыдущих ошибок подключения) | | Смотреть более ранние ошибки подключения/таймауты; проверить |
504 примерно через 60 секунд | Upstream не передавал данные в течение read timeout | | Найти медленный запрос через slowlog; как заплатку - поднять |
413 при загрузке файла или большом POST |
| | Поднять |
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слушающие,-xUnix,-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.owner,listen.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 - в http, server или 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.log:sudo 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, без перезапуска.
