Ошибки 404 и 502 в Nginx: разбор на живом примере
404 обычно означает, что ресурс не найден, а 502 — что Nginx не получил корректный ответ от приложения. На примере связки Nginx и сервиса на localhost разберём логи, конфигурацию и границы каждой ошибки.

На сайте за Nginx одновременно могут появиться две внешне похожие проблемы: 404 для отсутствующей страницы и 502 для приложения, которое не отвечает корректно. Первая чаще связана с маршрутизацией или файлами, вторая — с соединением между прокси и upstream-сервисом. Разберём связку Nginx 1.24+ и приложения на 127.0.0.1:8080: где возникает ошибка, что увидит пользователь и какие строки искать в логах.
Схема живого примера
Представим интернет-магазин, где Nginx принимает HTTPS-запросы, отдаёт статические файлы и отправляет динамические страницы приложению. Приложение слушает порт 8080 на том же сервере.
Клиент → Nginx :443 → приложение 127.0.0.1:8080
Запрос /catalog/item-42 должен уйти в приложение. Запрос к CSS-файлу Nginx обслуживает самостоятельно. Если файла или маршрута нет, ответ может сформировать сам Nginx либо приложение — это принципиально разные случаи.
Код 404 отвечает на вопрос «где ресурс?», а 502 — на вопрос «получил ли прокси нормальный ответ от сервера за ним?». Исправлять их нужно на разных уровнях.
[1] Upstream — сервер или группа серверов, которым Nginx передаёт запрос. В примере upstream — приложение на 127.0.0.1:8080.
Откуда берётся 404
404 Not Found означает, что сервер не нашёл ресурс по указанному адресу. Но источник ответа может быть разным:
- Nginx не нашёл статический файл в каталоге, заданном через
rootилиalias; - приложение получило запрос, но не знает такого маршрута;
- в ссылке есть ошибка: лишний слеш, неправильный slug или старый URL;
- правило
try_filesотправило запрос не туда; - Nginx перехватил ответ приложения и заменил его своей страницей ошибки.
По одному статусу в браузере причину не определить. Сначала проверьте заголовок Server, тело ответа и access log. Если приложение добавляет собственный JSON с полем message, это часто указывает на его 404. Но надёжнее сравнить запрос с логами upstream.
Два разных сценария
Статический файл можно проверять напрямую:
curl -i https://example.com/assets/app.css
Если файла нет, Nginx обычно пишет в error log сообщение вроде open() ... failed (2: No such file or directory). Путь в сообщении покажет, в каком каталоге Nginx искал файл.
Для динамического маршрута запрос проходит дальше:
curl -i https://example.com/catalog/item-42
Если приложение само вернуло 404, в access log Nginx появится статус 404, а переменная $upstream_status также обычно покажет 404. Если upstream не участвовал, значение upstream-статуса будет пустым.
Откуда берётся 502
502 Bad Gateway означает, что Nginx выступал прокси, но не получил от upstream корректный ответ. На практике встречаются четыре причины:
- приложение остановлено, а порт закрыт;
- в
proxy_passуказан неправильный адрес или порт; - процесс принял соединение, но оборвал его до отправки HTTP-ответа;
- upstream отправил ответ, который Nginx не смог разобрать, например повреждённые или некорректные заголовки.
Для остановленного сервиса в error log можно увидеть сообщение наподобие connect() failed (111: Connection refused) while connecting to upstream. Точный текст зависит от операционной системы. Если upstream слишком долго отвечает, чаще появляется 504 Gateway Timeout, а не 502: эти статусы не стоит смешивать.
| Что произошло | Что обычно видит клиент | Что искать в логах |
|---|---|---|
| Файл не найден Nginx | 404 | open() failed, путь к файлу |
| Маршрут не найден в приложении | 404 | upstream status 404, запись приложения |
| Приложение не слушает порт | 502 | connect() failed |
| Приложение слишком долго отвечает | 504 | upstream timed out |
| Приложение отправило некорректный ответ | 502 | upstream sent invalid header или похожее сообщение |
Конфигурация на примере Nginx 1.24+
Ниже — рабочий фрагмент для сайта, где статические файлы лежат в /var/www/site, а приложение доступно на порту 8080. Директива internal запрещает пользователю открыть техническую страницу ошибки напрямую по её внутреннему URI.
http {
log_format main '$remote_addr - $request '
'status=$status upstream_status=$upstream_status '
'request_time=$request_time '
'upstream_time=$upstream_response_time';
upstream shop_app {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name example.com;
root /var/www/site;
access_log /var/log/nginx/shop.access.log main;
error_log /var/log/nginx/shop.error.log warn;
error_page 404 /errors/404.html;
error_page 502 503 504 /errors/50x.html;
location = /errors/404.html {
internal;
}
location = /errors/50x.html {
internal;
}
location / {
proxy_pass http://shop_app;
proxy_intercept_errors on;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /assets/ {
try_files $uri =404;
}
}
}
Файлы /var/www/site/errors/404.html и /var/www/site/errors/50x.html должны существовать. После изменения проверьте конфигурацию и только затем перечитайте её процессом Nginx:
nginx -t
systemctl reload nginx
Проверка успешна, если nginx -t заканчивается сообщением syntax is ok и test is successful, а reload не возвращает ошибку.
Зачем нужен proxy_intercept_errors
Без proxy_intercept_errors on Nginx обычно передаёт клиенту ошибочный ответ upstream как есть. С включённой директивой Nginx может применить заданное через error_page правило к ответам приложения. Это удобно для единого дизайна ошибок, но меняет поведение: полезные JSON-ответы API могут превратиться в HTML-страницу.
Для API такую настройку часто не включают глобально. Страницу для человека можно оставить в отдельной location, а API вернуть его собственный структурированный ответ. Иначе клиентское приложение вместо JSON получит HTML и начнёт падать уже на этапе разбора ответа.
Как локализовать проблему за пять минут
- Повторите запрос с заголовками. Выполните
curl -iс тем же URL, методом и параметрами. Запишите статус, заголовки и время ответа. - Сверьте access log. Смотрите
status,upstream_status,request_timeиupstream_time. Если upstream-статус пуст, запрос мог не дойти до приложения. - Откройте error log в момент запроса. Для 502 ищите соединение с upstream, таймаут или некорректный ответ. Для 404 — путь к файлу и место, где Nginx выполнял поиск.
- Проверьте приложение напрямую. Выполните запрос к
http://127.0.0.1:8080с сервера. Если прямой запрос не работает, исправлять нужно сервис или его порт, а не внешний прокси. - Проверьте маршрутизацию. Для 404 сравните реальный путь файла, значение
root, правилаtry_filesи маршруты приложения. После изменения повторите тот же запрос, а не только откройте главную страницу.
Где схема ломается
404 маскирует ошибку маршрута
Если приложение использует клиентский роутер, например для одностраничного интерфейса, прямой переход на /orders/42 может дать 404: Nginx ищет файл orders/42, хотя маршрут должен обработать JavaScript. В таком случае обычно настраивают fallback на файл приложения, но делать это для API нельзя: опечатка в API-маршруте не должна превращаться в HTML главной страницы.
502 появляется только после деплоя
Частая причина — новая версия приложения слушает другой порт, а конфигурация Nginx осталась прежней. Другая — сервис запущен в контейнере, но в proxy_pass указан адрес, доступный только с хоста или только из контейнера. Проверяйте сетевой контур из того же окружения, где работает Nginx.
Единая страница ошибок скрывает диагностику
Пользователю нужна короткая страница без трассировки и внутренних адресов. Инженеру нужны access log, error log и логи приложения. Не выводите стек вызовов, имя внутреннего сервиса и параметры подключения в HTML-ответе: это помогает атакующему и не исправляет причину сбоя.
Что измерять в проде
Не подменяйте диагностику красивой страницей. Для каждого запроса сохраняйте конечный статус, статус upstream и длительность обработки. Поля $request_time и $upstream_response_time позволяют отличить медленное приложение от задержки до него. Сравнивайте их по времени и endpoint, а не только смотрите среднее значение: редкие 502 и 504 могут исчезать в общей статистике.
Отдельно учитывайте долю 404. Резкий рост может означать сломанные ссылки после релиза, удалённые карточки, ошибку CDN-кеша или автоматическое сканирование. Рост 502 чаще сигнализирует о проблеме деплоя, перезапусках процесса, лимите соединений или несовместимом ответе приложения.
Что делать дальше
Сначала добавьте в access log как минимум $status, $upstream_status и два времени. Затем создайте два теста: запрос к заведомо отсутствующему статическому файлу и запрос к рабочему динамическому маршруту при остановленном приложении. Зафиксируйте, какие строки появляются в обоих логах. После этого настройте алерт не на любую 404, а на аномальный рост 502 и 504 с разбивкой по upstream.
Правила error_page и proxy_intercept_errors стоит сверить с официальной документацией перед переносом в production: особенно если через тот же Nginx идут API-запросы и обычные HTML-страницы.
Разбор научной статьи: Сложный текст — понятным языком, с терминами
Спросить за 12 ₽Источники: Nginx documentation: HTTP proxy module, Nginx documentation: HTTP core module
Частые вопросы
Чем 404 от Nginx отличается от 404 приложения?
Почему Nginx показывает 502, если сайт открывается напрямую на порту приложения?
Всегда ли таймаут upstream означает 502?
Можно ли одной страницей обработать и 404, и 502?
Почему после включения proxy_intercept_errors API перестал возвращать JSON?
Нужно ли увеличивать proxy_read_timeout, если появляется 502?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.