Логи Nginx: как смотреть и анализировать

30.04.2026
18:53

Nginx пишет два вида логов: access.log фиксирует каждый входящий запрос, error.log — всё что пошло не так. Вместе они дают полную картину происходящего на сервере: кто приходит, что запрашивает, какой код ответа получает, сколько времени уходит на обработку, почему падают ошибки.

Уметь читать эти логи — базовый навык для всех кто работает с веб-серверами. Упал сайт — открываете error.log. Подозрительный трафик — смотрите access.log. Медленные страницы — ищете в логах запросы с большим временем ответа. Эта статья разбирает все инструменты: от простого tail -f до автоматической аналитики через GoAccess.

Где находятся логи Nginx

По умолчанию Nginx пишет логи в:

  • /var/log/nginx/access.log — журнал всех запросов
  • /var/log/nginx/error.log — журнал ошибок

Это дефолтные пути для Ubuntu, Debian, CentOS. На других дистрибутивах или при кастомной сборке пути могут отличаться.

Проверяет где реально настроены логи в конфиге Nginx:

grep -r "access_log\|error_log" /etc/nginx/

Если для каждого виртуального хоста настроены отдельные логи (так делать правильно), найдёте что-то вроде:

/etc/nginx/sites-enabled/example.com:    access_log /var/log/nginx/example.com-access.log;
/etc/nginx/sites-enabled/example.com:    error_log  /var/log/nginx/example.com-error.log;

Смотрит все лог-файлы Nginx с размерами:

ls -lh /var/log/nginx/

Формат access.log

Прежде чем анализировать логи нужно понять их структуру. Каждая строка access.log — одна запись об одном HTTP запросе.

Показывает последние 5 строк access.log:

tail -5 /var/log/nginx/access.log

Типичная строка выглядит так:

192.168.1.1 - john [30/Apr/2026:14:23:01 +0300] "GET /api/users HTTP/1.1" 200 1543 "https://example.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"

Разбор по полям:

Поле Пример Описание
$remote_addr 192.168.1.1 IP адрес клиента
$remote_user john Имя пользователя (при HTTP auth, иначе -)
$time_local 30/Apr/2026:14:23:01 +0300 Время запроса
$request GET /api/users HTTP/1.1 Метод, URI, версия HTTP
$status 200 HTTP код ответа
$body_bytes_sent 1543 Размер ответа в байтах (без заголовков)
$http_referer https://example.com/dashboard Откуда пришёл запрос
$http_user_agent Mozilla/5.0 ... Браузер / клиент

Это формат combined — стандартный для большинства установок. Определён в конфиге через директиву log_format combined.

Проверяет какой формат используется для конкретного лог-файла:

grep -A5 "log_format" /etc/nginx/nginx.conf

Просмотр логов в реальном времени

Следить за новыми запросами

Выводит последние 50 строк и показывает новые по мере появления:

tail -f /var/log/nginx/access.log

Открывает несколько файлов одновременно (access + error):

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

Выводит последние N строк (например 200):

tail -200 /var/log/nginx/access.log

Смотреть логи с фильтрацией в реальном времени

Показывает в реальном времени только ошибки (коды 4xx и 5xx):

tail -f /var/log/nginx/access.log | grep -E '" [45][0-9]{2} '

Показывает только запросы от конкретного IP:

tail -f /var/log/nginx/access.log | grep "192.168.1.100"

Показывает только POST запросы:

tail -f /var/log/nginx/access.log | grep '"POST '

Смотреть логи через less

less удобен когда нужно листать большой файл без загрузки всего в память.

Открывает лог файл для просмотра с навигацией:

less /var/log/nginx/access.log

Управление в less: G — перейти в конец, g — в начало, /паттерн — поиск вперёд, ?паттерн — поиск назад, n — следующее совпадение, q — выход.

Открывает файл в режиме follow (аналог tail -f внутри less):

less +F /var/log/nginx/access.log

Ctrl+C — вернуться в режим навигации из режима follow.

Анализ access.log через grep и awk

Фильтрация по коду ответа

Считает количество запросов с каждым кодом ответа:

awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn

Показывает только запросы вернувшие 500:

grep '" 500 ' /var/log/nginx/access.log

Показывает все ошибки 5xx с URL и временем:

grep -E '" 5[0-9]{2} ' /var/log/nginx/access.log | awk '{print $4, $7, $9}'

Считает ошибки 4xx за последний час (если временная метка в стандартном формате):

awk -v d="$(date +'%d/%b/%Y:%H')" '$4 ~ d && $9 ~ /^4/' /var/log/nginx/access.log | wc -l

Анализ по IP адресам

Выводит топ-20 IP адресов по количеству запросов:

awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

Показывает все запросы от конкретного IP:

grep "^203.0.113.42 " /var/log/nginx/access.log

Считает уникальные IP адреса за всё время:

awk '{print $1}' /var/log/nginx/access.log | sort -u | wc -l

Находит IP адреса сделавшие больше 1000 запросов (потенциальный DDoS или бот):

awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | awk '$1 > 1000'

Анализ по URL

Выводит топ-20 самых запрашиваемых URL:

awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

Показывает топ URL только для ошибок 404:

grep '" 404 ' /var/log/nginx/access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -20

Показывает URL с параметрами запроса очищенными (только путь без query string):

awk '{print $7}' /var/log/nginx/access.log | cut -d'?' -f1 | sort | uniq -c | sort -rn | head -20

Анализ трафика по времени

Считает количество запросов по часам:

awk '{print $4}' /var/log/nginx/access.log | cut -d: -f2 | sort | uniq -c

Считает запросы по минутам за последний час:

awk '{print $4}' /var/log/nginx/access.log | cut -d: -f1-3 | sort | uniq -c | tail -60

Показывает пиковую нагрузку — минута с наибольшим числом запросов:

awk '{print $4}' /var/log/nginx/access.log | cut -d: -f1-3 | sort | uniq -c | sort -rn | head -5

Анализ объёма трафика

Считает суммарный объём отданных данных в байтах:

awk '{sum += $10} END {print sum " bytes"}' /var/log/nginx/access.log

Считает суммарный трафик в мегабайтах:

awk '{sum += $10} END {print sum/1024/1024 " MB"}' /var/log/nginx/access.log

Выводит топ URL по суммарному объёму трафика:

awk '{traffic[$7] += $10} END {for (url in traffic) print traffic[url], url}' /var/log/nginx/access.log | sort -rn | head -20

Анализ User-Agent

Выводит топ браузеров и ботов:

awk -F'"' '{print $6}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

Показывает только запросы от ботов (Googlebot, ботов мониторинга и т.д.):

grep -i "bot\|crawler\|spider" /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn

Показывает запросы без User-Agent (часто сканеры и атаки):

grep '" "-"$' /var/log/nginx/access.log | wc -l

Анализ времени ответа

Если в лог-формате включено поле $request_time, можно находить медленные запросы.

Сначала проверяете есть ли поле request_time в формате:

grep "request_time\|upstream_response_time" /etc/nginx/nginx.conf

Если оно есть (обычно последнее поле), выводит топ-20 самых медленных запросов:

awk '{print $NF, $7}' /var/log/nginx/access.log | sort -rn | head -20

Показывает запросы с временем ответа больше 2 секунд:

awk '$NF > 2.0 {print $NF"s", $7, $9}' /var/log/nginx/access.log | sort -rn | head -20

Считает среднее время ответа:

awk '{sum += $NF; count++} END {print "Avg:", sum/count, "sec"}' /var/log/nginx/access.log

Анализ error.log

Error.log устроен иначе — каждая запись содержит уровень серьёзности ошибки.

Показывает последние 50 строк error.log:

tail -50 /var/log/nginx/error.log

Типичная строка error.log:

2026/04/30 14:23:01 [error] 1234#1234: *567 connect() failed (111: Connection refused) while connecting to upstream, client: 192.168.1.1, server: example.com, request: "GET /api/data HTTP/1.1", upstream: "http://127.0.0.1:8080/api/data"

Поля: дата/время, уровень (debug, info, notice, warn, error, crit, alert, emerg), PID процесса, номер соединения, сообщение, детали запроса.

Фильтрация error.log по уровню

Показывает только критические ошибки (crit, alert, emerg):

grep -E '\[crit\]|\[alert\]|\[emerg\]' /var/log/nginx/error.log

Показывает только ошибки уровня error:

grep '\[error\]' /var/log/nginx/error.log | tail -50

Считает количество ошибок по типам:

grep -oP '\[\w+\]' /var/log/nginx/error.log | sort | uniq -c | sort -rn

Типичные ошибки и что они означают

Показывает ошибки подключения к upstream (бэкенд недоступен):

grep "connect() failed\|upstream" /var/log/nginx/error.log | tail -20

Это означает что приложение (Node.js, PHP-FPM, Python) не отвечает на порту. Смотрите статус самого приложения.

Показывает ошибки прав доступа к файлам:

grep "Permission denied\|No such file" /var/log/nginx/error.log | tail -20

Показывает ошибки SSL/TLS:

grep -i "ssl\|tls\|certificate" /var/log/nginx/error.log | tail -20

Показывает ошибки превышения лимитов (rate limiting, размер буфера):

grep -i "limit\|too large\|buffer" /var/log/nginx/error.log | tail -20

Считает уникальные сообщения об ошибках (убирает повторы с разными IP):

grep '\[error\]' /var/log/nginx/error.log | sed 's/client: [0-9.]*/client: X.X.X.X/' | sort | uniq -c | sort -rn | head -20

Работа со сжатыми логами

После ротации логи сжимаются в .gz архивы. Обычные команды на них не работают — нужны gz-варианты утилит.

Читает сжатый лог без распаковки:

zcat /var/log/nginx/access.log.1.gz

Ищет в сжатом логе:

zgrep "500" /var/log/nginx/access.log.1.gz

Просматривает сжатый лог через less:

zless /var/log/nginx/access.log.1.gz

Анализирует все логи за последнюю неделю включая сжатые:

zcat /var/log/nginx/access.log*.gz | awk '{print $9}' | sort | uniq -c | sort -rn

Анализирует текущий лог и все архивы одной командой:

cat /var/log/nginx/access.log <(zcat /var/log/nginx/access.log.*.gz 2>/dev/null) | awk '{print $1}' | sort | uniq -c | sort -rn | head -20

Готовые скрипты для частых задач

Быстрый отчёт по логу

Выводит сводку за текущий лог одной командой:

echo "=== Nginx Log Summary ===" && \
echo "Total requests: $(wc -l < /var/log/nginx/access.log)" && \
echo "Unique IPs: $(awk '{print $1}' /var/log/nginx/access.log | sort -u | wc -l)" && \
echo "--- Status codes ---" && \
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn && \
echo "--- Top 5 IPs ---" && \
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -5 && \
echo "--- Top 5 URLs ---" && \
awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -5

Поиск подозрительной активности

Показывает IP с более чем 500 запросами к несуществующим страницам (404):

grep '" 404 ' /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | awk '$1 > 500'

Находит признаки сканирования уязвимостей (попытки обратиться к типичным путям атак):

grep -E "\.php\?|wp-admin|\.env|\.git|etc/passwd|union.*select|<script" /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -20

Показывает запросы с аномально большим размером (возможная попытка загрузки больших файлов или атака):

awk '$10 > 10000000 {print $10/1024/1024 "MB", $1, $7}' /var/log/nginx/access.log | sort -rn | head -10

Мониторинг ошибок в реальном времени с уведомлением

Скрипт отправляет алерт если за минуту больше 50 ошибок 500:

cat > /usr/local/bin/nginx-error-monitor.sh << 'EOF'
#!/bin/bash
THRESHOLD=50
EMAIL="admin@your-domain.com"
LOG="/var/log/nginx/access.log"

COUNT=$(awk -v time="$(date +'%d/%b/%Y:%H:%M')" '$4 ~ time && $9 == "500"' "$LOG" | wc -l)

if [ "$COUNT" -gt "$THRESHOLD" ]; then
    echo "ALERT: $COUNT HTTP 500 errors in last minute on $(hostname)" | \
    mail -s "Nginx 500 spike: $COUNT errors" "$EMAIL"
fi
EOF
chmod +x /usr/local/bin/nginx-error-monitor.sh
echo "* * * * * root /usr/local/bin/nginx-error-monitor.sh" >> /etc/crontab

GoAccess — интерактивная аналитика в терминале

Grep и awk хороши для точечных запросов. Для полноценной аналитики удобнее GoAccess — инструмент который строит интерактивный дашборд прямо в терминале.

Устанавливает GoAccess:

apt install goaccess -y    # Ubuntu/Debian
dnf install goaccess -y    # CentOS/AlmaLinux

Запускает интерактивный анализ access.log:

goaccess /var/log/nginx/access.log --log-format=COMBINED

Открывается TUI-интерфейс с разделами: запросы, уникальные посетители, статические файлы, коды ответов, хосты, браузеры, операционные системы, ссылающиеся сайты, страны.

Анализирует несколько файлов включая архивы:

zcat /var/log/nginx/access.log.*.gz | goaccess - --log-format=COMBINED

Генерирует HTML-отчёт который можно открыть в браузере:

goaccess /var/log/nginx/access.log --log-format=COMBINED -o /var/www/html/report.html

Генерирует HTML-отчёт в реальном времени с автообновлением (WebSocket):

goaccess /var/log/nginx/access.log --log-format=COMBINED -o /var/www/html/report.html --real-time-html

После этого открываете браузер на /report.html — видите живую аналитику которая обновляется при появлении новых запросов.

Кастомный формат логов

Стандартный combined формат не включает несколько полезных полей — время ответа, имя хоста, статус upstream. Кастомный формат добавляет их.

Открываете /etc/nginx/nginx.conf и добавляете в блок http:

http {
    log_format extended '$remote_addr - $remote_user [$time_local] '
                        '"$request" $status $body_bytes_sent '
                        '"$http_referer" "$http_user_agent" '
                        '$request_time $upstream_response_time '
                        '$host "$http_x_forwarded_for"';

    access_log /var/log/nginx/access.log extended;
}

Что добавилось:

  • $request_time — время обработки запроса в секундах
  • $upstream_response_time — время ответа бэкенда
  • $host — имя хоста (полезно при нескольких виртуальных хостах в одном логе)
  • $http_x_forwarded_for — реальный IP если Nginx за балансировщиком

Проверяет конфиг и перезагружает Nginx:

nginx -t && systemctl reload nginx

Формат логов в JSON

JSON-формат удобен если логи собираются в централизованную систему (Elasticsearch, Loki, Splunk).

Добавляет JSON формат в nginx.conf:

log_format json_combined escape=json
    '{'
        '"time":"$time_iso8601",'
        '"remote_addr":"$remote_addr",'
        '"method":"$request_method",'
        '"uri":"$uri",'
        '"args":"$args",'
        '"status":$status,'
        '"bytes_sent":$body_bytes_sent,'
        '"request_time":$request_time,'
        '"upstream_time":"$upstream_response_time",'
        '"referer":"$http_referer",'
        '"user_agent":"$http_user_agent",'
        '"host":"$host"'
    '}';

access_log /var/log/nginx/access.json json_combined;

После применения каждая строка лога — валидный JSON:

{"time":"2026-04-30T14:23:01+03:00","remote_addr":"192.168.1.1","method":"GET","uri":"/api/users","args":"page=1","status":200,"bytes_sent":1543,"request_time":0.023,"upstream_time":"0.021","referer":"https://example.com","user_agent":"Mozilla/5.0","host":"api.example.com"}

Ищет в JSON-логах через jq:

cat /var/log/nginx/access.json | jq 'select(.status >= 500)'

Считает медленные запросы (больше 1 секунды) из JSON-лога:

cat /var/log/nginx/access.json | jq 'select(.request_time > 1) | {uri, request_time, status}' | jq -s 'sort_by(-.request_time) | .[0:20]'

Ротация логов

Без ротации access.log со временем займёт весь диск. Logrotate автоматически архивирует и очищает старые файлы.

Смотрит текущую конфигурацию ротации Nginx:

cat /etc/logrotate.d/nginx

Типичный конфиг который создаётся при установке Nginx:

/var/log/nginx/*.log {
    daily
    missingok
    rotate 52
    compress
    delaycompress
    notifempty
    create 0640 www-data adm
    sharedscripts
    postrotate
        if [ -f /var/run/nginx.pid ]; then
            kill -USR1 `cat /var/run/nginx.pid`
        fi
    endscript
}

kill -USR1 — это правильный способ сказать Nginx переоткрыть лог-файлы после ротации. Nginx не перезапускается, просто закрывает и открывает файловые дескрипторы.

Принудительно запускает ротацию для проверки конфига:

logrotate -f /etc/logrotate.d/nginx

Отдельные логи для каждого виртуального хоста:

# В блоке server для example.com
access_log /var/log/nginx/example.com-access.log combined;
error_log  /var/log/nginx/example.com-error.log warn;

Тогда logrotate подхватит их автоматически по маске *.log.

Отключение логов для статических файлов

Картинки, CSS, JS генерируют огромный поток записей в лог которые обычно не нужны для анализа. Их удобно отключить.

Добавляет в блок server в конфиге Nginx:

location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2|svg|webp)$ {
    access_log off;
    expires 30d;
    add_header Cache-Control "public, no-transform";
}

После перезагрузки Nginx статика перестаёт записываться в access.log — файл растёт медленнее, в нём остаётся только полезная информация.

Проверяет конфиг и применяет изменения:

nginx -t && systemctl reload nginx

Централизованный сбор логов

На нескольких серверах смотреть логи по отдельности неудобно. Централизованные решения собирают всё в одном месте.

Filebeat + Elasticsearch

Filebeat — лёгкий агент который читает файлы и отправляет данные в Elasticsearch или Logstash.

Устанавливает Filebeat:

curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-8.13.0-amd64.deb
dpkg -i filebeat-8.13.0-amd64.deb

Минимальная конфигурация /etc/filebeat/filebeat.yml для отправки Nginx логов:

filebeat.inputs:
  - type: log
    enabled: true
    paths:
      - /var/log/nginx/access.log
    fields:
      log_type: nginx_access

  - type: log
    enabled: true
    paths:
      - /var/log/nginx/error.log
    fields:
      log_type: nginx_error

output.elasticsearch:
  hosts: ["elasticsearch-server:9200"]

Promtail + Grafana Loki

Более лёгкая альтернатива — Loki от Grafana. Не индексирует содержимое логов (только метки), поэтому дешевле в хранении.

Конфигурация Promtail для сбора Nginx логов:

scrape_configs:
  - job_name: nginx
    static_configs:
      - targets:
          - localhost
        labels:
          job: nginx
          host: your-server
          __path__: /var/log/nginx/*.log

После настройки логи всех серверов видны в Grafana в едином интерфейсе с фильтрацией, поиском, алертами.

Часто задаваемые вопросы

Почему access.log пустой или не обновляется?

Три возможных причины. Первая — логирование отключено директивой access_log off в конфиге. Проверьте через grep -r "access_log off" /etc/nginx/. Вторая — Nginx пишет в другой файл: проверьте через grep -r "access_log" /etc/nginx/ | grep -v "off". Третья — проблемы с правами: ls -la /var/log/nginx/ покажет владельца файлов.

Как найти что положило сайт 10 минут назад?

Смотрите error.log за нужный период: grep "30/Apr/2026:14:" /var/log/nginx/error.log. Параллельно смотрите access.log на всплеск ошибок: grep "30/Apr/2026:14:" /var/log/nginx/access.log | awk '{print $9}' | sort | uniq -c. Если увидели резкий рост 502/504 — проблема в бэкенде. Резкий рост 499 — клиенты обрывали соединения (сервер тормозил). Резкий рост 500 — ошибки в приложении.

Как смотреть логи если Nginx стоит за CloudFlare или балансировщиком?

В поле $remote_addr будет IP CloudFlare или балансировщика, а не реального пользователя. Реальный IP передаётся в заголовке X-Forwarded-For или CF-Connecting-IP. Добавьте в формат лога поле $http_x_forwarded_for и анализируйте его. В конфиге Nginx также настройте set_real_ip_from с указанием IP диапазонов CloudFlare — тогда $remote_addr будет сразу реальным IP.

Что означает код 499 в логах?

499 — нестандартный код Nginx, означает что клиент закрыл соединение до того как сервер успел ответить. Обычно клиент устал ждать (таймаут на стороне клиента или браузера) и оборвал запрос. Массовые 499 — признак того что сервер отвечает слишком медленно. Смотрите $request_time рядом с 499 записями чтобы понять насколько долго ждал клиент.

Как узнать какой виртуальный хост получает больше всего трафика?

Если все хосты пишут в один файл, добавьте в формат лог поле $host. Тогда: awk '{print $host_field}' /var/log/nginx/access.log | sort | uniq -c | sort -rn. Правильнее настроить отдельный access.log для каждого server блока — тогда сразу видно трафик по каждому домену.

Как почистить логи без перезапуска Nginx?

Используйте truncate или пустое перенаправление: > /var/log/nginx/access.log. Nginx продолжит писать в тот же файловый дескриптор, данные будут записываться с начала очищенного файла. После очистки лучше отправить сигнал переоткрытия логов: nginx -s reopen или kill -USR1 $(cat /var/run/nginx.pid).

Итоги

Nginx логи — исчерпывающий источник информации о жизни сервера. Для повседневной работы хватает tail -f с grep для мониторинга в реальном времени и базовых awk команд для быстрой аналитики. GoAccess закрывает потребность в наглядных отчётах без установки тяжёлых систем. Для серьёзной инфраструктуры с несколькими серверами — Filebeat + Elasticsearch или Promtail + Loki дают централизованную картину.

Три вещи которые стоит настроить сразу: кастомный формат с $request_time для поиска медленных запросов, отдельные лог-файлы для каждого виртуального хоста, ротацию через logrotate чтобы логи не съели диск.

VPS на THE.Hosting с NVMe дисками в RAID-10 обеспечивает быструю запись логов даже при высоких нагрузках. При вопросах по настройке Nginx — поддержка доступна 24/7 через Telegram.

Содержание:
-15% на White и Black Pearl
На заказ нового выделенного сервера в США и Нидерландах
Выбрать сервер