Operations 37 min read

Practical Nginx Log Analysis: Uncovering the Culprit Behind Abnormal Access

This guide walks you through when to inspect Nginx logs, how to configure log formats, essential variables, common shell commands, and five real‑world troubleshooting scenarios—CC attacks, SQL injection, 500 errors, slow responses, and malicious crawlers—plus log rotation, tooling, and security hardening.

Ops Community
Ops Community
Ops Community
Practical Nginx Log Analysis: Uncovering the Culprit Behind Abnormal Access

Why and When to Analyze Nginx Logs

Log analysis is essential when you see slow page loads, unexplained 5xx errors, normal server load but frequent timeouts, security alerts for suspicious IPs, need to understand traffic sources, or suspect CC attacks or scans.

Nginx Log Format Configuration

Default Log Format

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;

The name main identifies the format; the variables inside define what is recorded.

Common Log Variables

$remote_addr

– client IP (or upstream IP if behind a proxy) $http_x_forwarded_for – real IP chain when proxies are used $remote_user – basic‑auth username ("-" if none) $time_local – local timestamp, e.g.

27/May/2026:10:30:15 +0800
$request

– full request line, e.g.

GET /api/user?id=1 HTTP/1.1
$status

– HTTP status code $body_bytes_sent – bytes sent to client (excluding headers) $http_referer – referer URL ("-" means direct access) $http_user_agent – client UA string $http_cookie – request cookies $request_time – total request processing time in seconds $upstream_response_time – time spent waiting for the upstream service $upstream_addr – upstream server address $pipe – "p" if request is pipelined, "." otherwise $server_name – matched server_name (virtual host) $request_filename – local file path of the request

JSON Log Format

log_format json_log escape=json '{'
'"time":"$time_iso8601",'
'"remote_addr":"$remote_addr",'
'"remote_user":"$remote_user",'
'"request":"$request",'
'"status":$status,'
'"body_bytes_sent":$body_bytes_sent,'
'"request_time":$request_time,'
'"upstream_response_time":"$upstream_response_time",'
'"http_referer":"$http_referer",'
'"http_user_agent":"$http_user_agent",'
'"http_x_forwarded_for":"$http_x_forwarded_for",'
'"request_filename":"$request_filename",'
'"uri":"$uri",'
'"args":"$args"'
'}';
access_log /var/log/nginx/access.json.log json_log;

Use escape=json to keep the output valid JSON and avoid quoting numeric fields.

Domain‑Specific Log Separation

server {
    listen 80;
    server_name example.com;
    access_log /var/log/nginx/example.com.access.log main;
    error_log /var/log/nginx/example.com.error.log;
    root /var/www/example.com;
}

server {
    listen 80;
    server_name api.example.com;
    access_log /var/log/nginx/api.example.com.access.log main;
    error_log /var/log/nginx/api.example.com.error.log;
    root /var/www/api.example.com;
}

This makes it easy to focus on a single domain’s logs.

Conditional Logging

# Do not log health checks, static assets, etc.
map $uri $loggable {
    default 1;
    /favicon.ico 0;
    /healthcheck 0;
    /robots.txt 0;
}
server {
    access_log /var/log/nginx/access.log main if=$loggable;
}

Only requests with $loggable equal to 1 are logged.

error_log Details

The error_log records Nginx internal errors. Levels (low to high): debug, info, notice, warn, error, crit, alert, emerg. Production typically uses warn or error to reduce noise. warn – upstream timeout or retry failures error – missing files, permission denied, upstream connection refused crit – connection limit reached, cannot open file descriptors emerg – Nginx cannot start (system‑level failure)

When reading error_log, focus on lines containing [error] or [crit] together with the client IP and request fields to pinpoint the offending request.

Common Analysis Commands

View basic file info : ls -lh /var/log/nginx/access.log, wc -l /var/log/nginx/access.log, tail -f /var/log/nginx/access.log PV/UV :

# PV (total requests)
wc -l /var/log/nginx/access.log
# UV (unique IPs)
awk '{print $1}' /var/log/nginx/access.log | sort -u | wc -l

Status‑code statistics :

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

Top IPs :

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

Slowest requests :

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

Hot URLs :

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

Per‑minute request count :

awk '{print substr($4,14,5)}' /var/log/nginx/access.log | sort | uniq -c | sort -k2 -n

User‑Agent statistics :

awk -F'
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Ops Community
Written by

Ops Community

A leading IT operations community where professionals share and grow together.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.