
Логи Nginx: диагностика 4xx, 5xx и медленных запросов
Настраиваем полезный access_log Nginx, читаем error_log, измеряем upstream timings и разбираем 404, 499, 502, 504 без записи секретов.
Содержание
Когда Nginx возвращает 502, код сообщает только о сбое при работе с приложением. Причину уточняют два журнала: access log связывает адрес, статус и длительность запроса, а error log записывает ошибку соединения, доступа к файлу или обработки конфигурации.
Начните с точного времени и одного воспроизводимого запроса. Затем найдите активные журналы, добавьте измерения работы upstream и сопоставьте запись Nginx с журналом приложения. Пароли, cookies, заголовок Authorization и тело запроса для этого не нужны и не должны попадать в лог.
Найдите активные журналы
Пути журналов могут переопределяться на уровнях http, server и location. Поэтому сначала найдите фактически подключённые директивы и последние сообщения службы:
sudo nginx -T 2>/dev/null | grep -E 'access_log|error_log|log_format'
systemctl status nginx --no-pager
sudo journalctl -u nginx --since "15 minutes ago"
nginx -T печатает всю собранную конфигурацию, поэтому выполняйте его в защищённой сессии: include могут содержать внутренние адреса. Не публикуйте вывод целиком.
В пакетной установке файлы часто находятся в /var/log/nginx, но полагайтесь на активную конфигурацию. При контейнерном запуске журналы могут направляться в stdout/stderr.
Добавьте формат с таймингами
Директива log_format задаётся в контексте http. Следующий формат добавляет время всего запроса, адрес upstream, его статус и три отдельных этапа ожидания:
log_format upstream_timing '$remote_addr [$time_iso8601] '
'"$request" status=$status bytes=$body_bytes_sent '
'rt=$request_time ua="$http_user_agent" '
'upstream=$upstream_addr us=$upstream_status '
'uct=$upstream_connect_time uht=$upstream_header_time '
'urt=$upstream_response_time rid=$request_id';
access_log /var/log/nginx/access.log upstream_timing;
$request_time включает обработку запроса Nginx до отправки последних байтов клиенту. $upstream_connect_time отражает соединение, $upstream_header_time — ожидание заголовка, $upstream_response_time — получение ответа upstream. При повторных попытках значения могут содержать несколько элементов, поэтому парсер должен поддерживать этот формат.
User-Agent полезен для диагностики, но создаёт дополнительный объём и пользовательские данные. Если он не нужен, исключите. Строка $request включает query string; токены не должны передаваться в URL. Для чувствительных endpoint рассмотрите отдельный минимальный формат.
Свяжите запрос с приложением
Nginx создаёт $request_id — идентификатор одного запроса. Передайте его приложению и верните в ответе, чтобы связать записи двух систем без поиска по адресу и секунде:
proxy_set_header X-Request-ID $request_id;
add_header X-Request-ID $request_id always;
Приложение должно безопасно записывать этот ID в свой журнал. Если внешний proxy уже выдаёт идентификатор, решите, доверять ли ему и как валидировать формат; не позволяйте произвольной длинной строке раздувать журналы.
Ответный заголовок помогает поддержке, но не должен раскрывать внутренний адрес или stack trace. Request ID — корреляционный ключ, не средство авторизации.
Как читать основные ошибки
404 Not Found
Смотрите выбранный server, URI, document root и порядок location. Для статики проверьте существование файла и права каждого каталога пути. Для приложения сравните его route и Nginx fallback. Статья про root, alias и try_files помогает разобрать выбор файла.
499
Клиент закрыл соединение. Сравните $request_time, upstream timings и timeout внешнего proxy. Если upstream почти всегда отвечает позже клиентского предела, исправляйте приложение или переводите долгую работу в фон. Один 499 от ушедшего пользователя не равен аварии.
502 Bad Gateway
В error log ищите connect() failed, permission denied, отсутствие socket, premature close или invalid header. Затем проверьте слушающие порты, состояние службы и её сообщения за тот же интервал:
sudo ss -ltnp
systemctl status myapp.service --no-pager
sudo journalctl -u myapp.service --since "15 minutes ago"
Не перезапускайте всё до чтения сообщения: у каждой причины своё исправление.
504 Gateway Timeout
Сравните uct, uht и urt. Долгий connect указывает на сеть или очередь соединений, долгий header — на начало обработки приложения, долгий полный ответ — на вычисление или передачу тела. proxy_read_timeout измеряет паузу между чтениями, поэтому его увеличение не является оптимизацией.
413 и 408
413 связан с размером тела, 408 — с чтением клиентского запроса. Для них используйте руководство по лимитам и таймаутам, а не настройки upstream наугад.
Фильтрация в командной строке
Для короткого окна можно прочитать конец error log и отфильтровать несколько серверных кодов в access log. Пути замените значениями из действующей конфигурации:
sudo tail -n 200 /var/log/nginx/error.log
sudo grep ' 50[234] ' /var/log/nginx/access.log | tail -n 50
Формат может отличаться, поэтому текстовый grep не заменяет парсер. Не запускайте тяжёлый поиск по всем архивам на заполненном production-диске. Сначала ограничьте файл и время.
Проверьте, сколько места занимают журналы и как пакет собирается их ротировать. Ключ -d у logrotate показывает план без изменения файлов:
sudo du -h /var/log/nginx/*
sudo logrotate -d /etc/logrotate.d/nginx
Режим -d выполняет отладку без изменений. После ротации Nginx должен корректно переоткрыть файлы штатным механизмом пакета. Простое удаление активного журнала может не освободить место, пока процесс держит дескриптор.
Проверка и откат
После добавления формата проверьте конфигурацию, выполните reload, запросите известный URL и прочитайте новую строку access log:
sudo nginx -t
sudo systemctl reload nginx
curl -I https://example.com/health
sudo tail -n 5 /var/log/nginx/access.log
Убедитесь, что поля появляются, пути доступны Nginx, а объём одной строки приемлем. Затем воспроизведите известные safe-сценарии 200 и 404. Ошибку upstream тестируйте только в контролируемой среде.
Если формат мешает сборщику или растёт слишком быстро, верните предыдущий access_log, проверьте конфигурацию и reload. Полезность журнала измеряется тем, насколько быстро он приводит к причине без утечки данных.
Первоисточники
- Nginx: ngx_http_log_module — log_format, access_log и переменные.
- Nginx: upstream module — значения upstream timing и status.
Самопроверка
Проверьте, что материал усвоен
Ответьте на все вопросы. Результат сохранится только в этом браузере и будет учтён в статистике прочитанных материалов.
Разбираем коротко
Частые вопросы
Чем access_log отличается от error_log Nginx?
Access log записывает обработанные запросы, статусы, объёмы и выбранные переменные. Error log содержит сообщения о проблемах конфигурации, файлах, соединениях с upstream и работе процессов Nginx.
Что означает статус 499 в журнале Nginx?
Это код Nginx для ситуации, когда клиент закрыл соединение до завершения ответа. Причиной может быть уход пользователя, таймаут внешнего proxy или слишком медленный backend; нужен анализ времени и соседних журналов.
Можно ли записывать тело запроса и Authorization для удобной диагностики?
Обычно нельзя: в них могут находиться пароли, токены и персональные данные. Логируйте технические метаданные и безопасный request ID, а чувствительную отладку выполняйте ограниченно и с последующим удалением.


