Как читать журналы доступа Nginx
Когда что-то ломается, журнал доступа рассказывает историю. Кто посетил сайт. Что они запрашивали. Какие URL-адреса не сработали. Когда начался всплеск. Я использовал эти шаги в реальном проекте, и они сработали. Примеры ниже безопасны для копирования и запуска, а затем их можно настроить под вашу конфигурацию.
Что такое журналы доступа Nginx?
Журналы доступа Nginx — это текстовые файлы, которые записывают каждый HTTP-запрос, получаемый вашим веб-сервером. Каждая строка представляет один запрос и содержит такие детали, как IP-адрес клиента, временная метка, HTTP-метод, путь к URL, код состояния, размер ответа и многое другое.
Эти журналы автоматически генерируются Nginx по мере обработки запросов. Каждый раз, когда кто-то посещает ваш сайт, нажимает на ссылку, отправляет форму или даже обращается к неработающему URL-адресу, Nginx записывает строку в журнал доступа. Это происходит в режиме реального времени, поэтому файл журнала постоянно растет.
Как работают журналы доступа
Nginx использует настраиваемый формат журнала, определенный в файле nginx.conf. Формат по умолчанию выглядит так:
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent"';
Это создает записи журнала, подобные этой:
192.168.1.100 - - [20/Aug/2025:10:30:45 +0000] "GET /blog/post HTTP/1.1" 200 1234 "https://example.com/" "Mozilla/5.0..."
Многие современные конфигурации используют более подробный формат, похожий на JSON, с парами ключ=значение, на который рассчитаны примеры в этой статье.
CDN против Origin: Где искать журналы
Если вы используете CDN (например, Cloudflare, CloudFront или Fastly), существует два разных набора журналов, которые следует учитывать:
Журналы CDN edge: показывают запросы от реальных пользователей, обращающихся к CDN. Они содержат реальные IP-адреса клиентов, пользовательские агенты и географическое положение.
Журналы исходного сервера (о чем эта статья): показывают запросы, которые прошли через CDN к вашему серверу Nginx. Эти журналы часто показывают IP-адрес CDN в качестве клиента, а не IP-адрес конечного пользователя.
Для устранения неполадок вам обычно сначала нужны журналы CDN. Проверяйте журналы исходного сервера только тогда, когда вам нужно увидеть, что на самом деле достигло вашего сервера, или при отладке проблем на стороне сервера.
Все примеры предполагают журналы в формате ключ=значение (поля вроде status="404" и request="GET /path HTTP/1.1"), а файлы находятся в /var/log/nginx/. При необходимости измените имена.
Что вы узнаете
- Как читать журналы в реальном времени и сжатые файлы за один раз
- Как находить лучшие IP-адреса и лучшие URL-адреса с ошибками
- Как получить все данные с одного IP-адреса
- Как сфокусироваться на коротком временном интервале
- Как связать журналы доступа с блокировками WAF
- Как сохранить результаты быстрыми и полезными
Быстрый старт
Прочитайте все, независимо от того, является ли файл .log или .gz:
zgrep -h . /var/log/nginx/ssl-*.access.log*
Этот трюк с zgrep работает как с обычными, так и с ротированными файлами. Добавьте фильтры после него.
Инструменты, которые вы будете использовать
zgrep: Похож на grep, но работает как с обычными файлами, так и со сжатыми (.gz) файлами. Идеально подходит для журналов, которые ротируются и сжимаются.
awk: Мощный инструмент обработки текста, который может разбивать строки по разделителям и извлекать определенные поля. Отлично подходит для разбора структурированных форматов журналов.
cut: Извлекает определенные столбцы или поля из текста. Используйте -d для указания разделителя (например, кавычек или запятых) и -f для выбора номера поля.
Найти лучшие IP-адреса, стоящие за штормами 404
Когда редакторы сообщают о «множестве битых ссылок», начните отсюда.
zgrep -h . /var/log/nginx/ssl-*.access.log* \
| awk -F'status="' '$2 ~ /^404"/' \
| awk -F'x_forwarded_for="' '{print $2}' \
| cut -d'"' -f1 | cut -d',' -f1 \
| LC_ALL=C sort | uniq -c | sort -nr | head
Почему это помогает. Вы видите худших нарушителей первыми. Если IP-адрес сканирует случайные пути, вы быстро его заметите.
Посмотреть все, что сделал один IP-адрес
Полезно, когда вам нужно объяснить блокировку или всплеск с одного источника.
IP="112.134.209.112"
zgrep -h . /var/log/nginx/ssl-*.access.log* \
| grep -F 'x_forwarded_for="'$IP
Совет. В некоторых стеках IP-адрес клиента может находиться в src или src_ip. Если это так, измените имя поля в grep.
Лучшие URL-адреса, запрашиваемые этим IP-адресом (игнорировать строки запроса)
IP="112.134.209.112"
zgrep -h . /var/log/nginx/ssl-*.access.log* \
| grep -F 'x_forwarded_for="'$IP \
| awk -F'request="' '{print $2}' | cut -d'"' -f1 \
| awk '{print $2}' | cut -d'?' -f1 \
| LC_ALL=C sort | uniq -c | sort -nr | head
Почему это помогает. Удаление строки запроса группирует «одну и ту же страницу с разными параметрами», что делает закономерности очевидными.
Лучшие URL-адреса с ошибками по статусу
404:
zgrep -h . /var/log/nginx/ssl-*.access.log* \
| awk -F'status="' '$2 ~ /^404"/' \
| awk -F'request="' '{print $2}' | cut -d'"' -f1 \
| awk '{print $2}' | cut -d'?' -f1 \
| LC_ALL=C sort | uniq -c | sort -nr | head
5xx:
zgrep -h . /var/log/nginx/ssl-*.access.log* \
| awk -F'status="' '{split($2,a,"\""); if (a[1] ~ /^5[0-9][0-9]$/) print}' \
| awk -F'request="' '{print $2}' | cut -d'"' -f1 \
| awk '{print $2}' | cut -d'?' -f1 \
| LC_ALL=C sort | uniq -c | sort -nr | head
Почему это помогает. Это дает вам ранжированный список проблемных путей. Исправив первые пять, вы часто устраняете большую часть проблем.
Сфокусироваться на коротком временном интервале
Когда происходит всплеск в известное время, вам нужны данные до и после.
Просто и быстро (достаточно для 10-минутного окна в пределах одного часа):
# Пример: 09/Aug/2025 23:15–23:25
zgrep -h 'time_local="' /var/log/nginx/ssl-*.access.log* \
| egrep '09/Aug/2025:23:1[5-9]|09/Aug/2025:23:2[0-5]'
Передайте результат в любой из приведенных выше подсчетов.
Почему это помогает. Вы сравниваете трафик непосредственно перед и сразу после события, что часто является всем, что вам нужно, чтобы увидеть, что изменилось.
POST-запросы форм и большие тела
Большие POST-запросы к форме могут вызывать срабатывание правил WAF. Измерьте их.
PATH_RE="/about/.*speaker-request-form"
zgrep -h . /var/log/nginx/ssl-*.access.log* \
| awk -F'request="' -v r="$PATH_RE" '
{ split($2,a,"\""); split(a[1],b," "); m=b[1]; u=b[2];
if (m=="POST" && u ~ r) print }' \
| awk -F'request_length="' '{print $2}' | cut -d'"' -f1 \
| awk '{sum+=$1; c++} END{print "POST count="c, "avg_request_length=" (c?sum/c:0)}'
Почему это помогает. Если средний размер запроса высок, ваш WAF может блокировать проверку. Теперь у вас есть доказательства и цифры.
Связать журналы доступа с блокировками WAF
- Запишите точное время и правило из события WAF.
- Отфильтруйте журналы доступа за этот период, используя трюк с «временным окном».
- Ищите тот же путь, тот же IP-адрес или большой request_length.
- Если правило WAF говорит о размере тела или ограничениях проверки, подтвердите с помощью анализа POST выше.
Это замыкает цикл. Вы можете сказать, что произошло и почему это произошло.
Важные советы по скорости
- Фильтруйте рано. Добавьте grep 'status="404"' перед сортировкой.
- Используйте LC_ALL=C sort для более быстрой и стабильной сортировки.
- Удаляйте строки запроса, чтобы группировать страницы по пути.
- Сначала возьмите выборку. Запустите head в конвейере, чтобы проверить, правильно ли вы выбираете нужное поле.
- Документируйте свой log_format в репозитории. Будущее вы скажет вам спасибо.
Распространенные ошибки
- Подсчет по remote_addr, когда ваше приложение находится за прокси-сервером. Используйте x_forwarded_for или ваше настоящее поле IP-адреса клиента.
- Предположение, что все файлы журналов используют один и тот же формат. Проверьте свой log_format в nginx.conf перед разбором.
- Забывание, что ротированные журналы (файлы .gz) требуют zgrep, а не grep.
- Использование сложных регулярных выражений, которые ломаются при изменении форматов журналов. Сохраняйте простоту.
Выводы
Журналы быстро отвечают на реальные вопросы. Начните с четкого вопроса, передавайте только то, что вам нужно, и считайте. Свяжите то, что вы видите в Nginx, с тем, что сообщает ваш WAF. Вы перейдете от «что-то казалось медленным» к «этот URL-адрес 2379 раз возвращал ошибку с одного IP-адреса за десять минут, и вот исправление».