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.
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 -lStatus‑code statistics :
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rnTop IPs :
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20Slowest requests :
awk '{print $NF, $0}' /var/log/nginx/access.log | sort -rn | head -20Hot URLs :
awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -30Per‑minute request count :
awk '{print substr($4,14,5)}' /var/log/nginx/access.log | sort | uniq -c | sort -k2 -nUser‑Agent statistics :
awk -F'Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Ops Community
A leading IT operations community where professionals share and grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
