Nginx 访问日志分析:用几行命令看懂流量

访问日志不只是「记录下来」,稍微配一下格式,再用几行 awk,就能看出站点的大部分问题。

一、先把格式配好

默认的 combined 格式信息不够,建议加上耗时:

nginx
log_format main escape=json
'{
  "time":"$time_iso8601",
  "remote_addr":"$remote_addr",
  "request":"$request",
  "status": "$status",
  "body_bytes_sent":"$body_bytes_sent",
  "request_time":"$request_time",
  "upstream_response_time":"$upstream_response_time",
  "http_referrer":"$http_referer",
  "http_user_agent":"$http_user_agent",
  "http_x_forwarded_for":"$http_x_forwarded_for"
}';

access_log /var/log/nginx/access.log main;

JSON 格式的好处是能用 jq 精确处理:

bash
tail -1 /var/log/nginx/access.log | jq .

# 找出所有超过 1 秒的请求
jq -select(.request_time > 1) /var/log/nginx/access.log

不想改格式的话,用默认的也能分析,只是字段位置要数一下。

二、默认格式的字段位置

bash
# combined 格式:
# 1.2.3.4 - - [19/Feb/2026:10:00:01 +0800] "GET /path HTTP/1.1" 200 1234 "-" "Mozilla/5.0"
#   $1      $2 $3  $4                       $5   $6    $7       $8  $9   $10   $11...

用 awk 时:

内容awk 变量
IP$1
请求行$6 $7 $8(合并后)
状态码$9
字节数$10

三、常用统计

bash
LOG=/var/log/nginx/access.log

# 总 PV
wc -l < $LOG

# 独立 IP 数(UV 近似)
awk '{print $1}' $LOG | sort -u | wc -l

# TOP 20 IP
awk '{print $1}' $LOG | sort | uniq -c | sort -rn | head -20

# TOP 20 被请求的 URL
awk '{print $7}' $LOG | sort | uniq -c | sort -rn | head -20

# 状态码分布
awk '{print $9}' $LOG | sort | uniq -c | sort -rn

# 只看 4xx/5xx
awk '$9 >= 400 {print $9, $7}' $LOG | sort | uniq -c | sort -rn | head -20

# 404 有哪些(找出坏链接)
awk '$9 == 404 {print $7}' $LOG | sort | uniq -c | sort -rn | head -20

# 流量总量(字节)
awk '{s += $10} END {print s/1024/1024 " MB"}' $LOG

# 按小时看请求量
awk '{print substr($4, 14, 2)}' $LOG | sort | uniq -c

四、找慢请求

如果用了带 $request_time 的格式:

bash
# 最慢的 20 个请求
awk '{print $NF, $7}' $LOG | sort -rn | head -20

# 超过 1 秒的请求数占比
awk '{if ($NF > 1) slow++; total++} END {print slow "/" total}' $LOG

# 按 URL 平均耗时
awk '{t[$7] += $NF; c[$7]++} END {for (u in t) printf "%.3f %s\n", t[u]/c[u], u}' $LOG \
  | sort -rn | head -20

JSON 格式的话更清晰:

bash
jq -r "select(.request_time > 1) | [.request_time, .status, .request] | @tsv" $LOG \
  | sort -rn | head -20

五、识别异常访问

bash
# 单 IP 请求数异常高(可能是爬虫或攻击)
awk '{print $1}' $LOG | sort | uniq -c | sort -rn | awk '$1 > 1000'

# 扫目录的特征:大量 404 且路径含敏感词
awk '$9 == 404 {print $1}' $LOG | sort | uniq -c | sort -rn | head -10
grep -E "wp-login|phpmyadmin|\.env|adminer|xmlrpc" $LOG | awk '{print $1}' | sort | uniq -c

# 看 User-Agent 有没有可疑的扫描器
awk -F\" '{print $6}' $LOG | sort | uniq -c | sort -rn | head -20

# 找 SQL 注入尝试的特征
grep -iE "union.*select|information_schema|sleep\(" $LOG | head -10

发现异常 IP 后:

bash
sudo ufw deny from 1.2.3.4
sudo fail2ban-client set sshd banip 1.2.3.4

六、按天切割日志做对比

logrotate 已经按天切了,直接对比:

bash
ls /var/log/nginx/access.log*

# 昨天 vs 今天的 PV
wc -l < /var/log/nginx/access.log.1
wc -l < /var/log/nginx/access.log

# 解压看更早的
zcat /var/log/nginx/access.log.2.gz | wc -l

七、实时看

bash
# 实时看请求(只看 URL 和状态码,清爽)
tail -f /var/log/nginx/access.log | awk '{print $7, $9}'

# 实时看非 200 的请求
tail -f /var/log/nginx/access.log | awk '$9 != 200 {print $1, $7, $9}'

八、日志不用自己分析也行

如果嫌命令麻烦,这些工具能直接出图表:

工具特点
goaccess命令行实时 + 生成 HTML 报告,装上就能用
Cloudflare Analytics零配置,看趋势够用
Plausible / Umami自托管统计,注重隐私
bash
sudo apt install -y goaccess
goaccess /var/log/nginx/access.log --log-format=COMBINED

# 生成一份 HTML 报告慢慢看
goaccess /var/log/nginx/access.log -o /tmp/report.html --log-format=COMBINED

我自己的做法是:日常看 Cloudflare 面板,要排查具体问题时再上 awk。两个都备着最好用。