博客上线之后我一直在用自己写的访问统计插件看数据。但那套东西是"存进数据库再查",而服务器上其实还有一份更原始、更完整的记录——Nginx 的访问日志

这篇笔记整理怎么用几个命令直接分析日志。不需要装任何软件,Linux 自带的 awk、sort、uniq 就够了。

日志长什么样

Nginx 默认的访问日志格式(combined)每一行长这样:

1.2.3.4 - - [19/Sep/2026:19:45:06 +0800] "GET /index.php/archives/26/ HTTP/1.1" 200 13193 "-" "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 ..."

按空格切分,各字段依次是:

字段含义
$1客户端 IP
$4时间戳(含时区)
$6 $7 $8请求方法、路径、协议
$9HTTP 状态码
$10响应体字节数
$11来源页面(Referer)
$12User-Agent(中间有空格,会占多个字段)

记住这个位置表,后面所有命令都是围绕"取第几列"展开的。

我这份日志今天一天有 1997 行,来自 220 个不同的 IP

六个常用统计

1. 总访问量(PV)

wc -l /var/log/nginx/access.log

最简单的一个——日志一行就是一次请求,行数就是 PV。

2. 独立访客数(UV)

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

拆开看这三步:

awk '{print $1}' 取出所有 IP;sort -u 排序并去重(-u 是 unique);wc -l 数行数。

注意这里统计的是"独立 IP 数",不是严格意义上的独立访客——同一个办公室里多台设备共用一个出口 IP 会被算成 1 个。这一点的讨论我写在统计插件那篇里了。

3. 访问量最高的页面

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

这个管道链是日志分析的经典套路,值得逐段理解:

awk '{print $7}' 取出请求路径

sort 先排序——这一步不能省,因为 uniq 只能合并相邻的重复行

uniq -c 去重并统计每项出现次数

sort -rn 按数字倒序(-n 数字排序,-r 降序)

head -20 只看前 20 条

如果漏掉第一个 sortuniq -c 会把同一个路径算成好多个不同的组——因为相同的行没有挨在一起。这是新手最常见的错误。

4. 状态码分布

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

只看 $9 换成状态码就行。输出大概是:

   1712 200
    130 404
     58 301
     22 403
     15 500

这份数据里值得注意的两点:

404 有 130 次。对于一个只有十几个页面的小站,这个数字偏高。查一下是哪些路径——大部分应该是扫描器在探测不存在的文件(下面会说到),但也有可能是站内真的有坏链接。

500 有 15 次。5xx 是服务端错误,说明 PHP 执行出问题了,需要去看 PHP 的错误日志。这个必须查。

5. 流量来源

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

$11 是 Referer。直接访问的会显示 "-"

这个统计能回答"访客是从哪来的"——搜索引擎、还是某个外链。

6. 响应体总流量

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

把所有响应的字节数累加,换算成 MB。END 块在 awk 处理完所有行后执行一次,用来输出汇总结果。

这个数字对按流量计费的服务器很有用——我的阿里云 ECS 就是按流量计费的,超了要额外掏钱。定期看一眼这个数,心里有底。

异常排查:从日志里发现扫描器

这是我觉得日志分析最有价值的地方——能看到访客看不到的东西

我随手翻日志的时候注意到几行很可疑的记录:

1.2.3.4 - - [19/Sep/2026:19:41:37 +0800] "GET //images/images/images/images/images/images/cache.php HTTP/1.1" 301 178 "www.google.com" "Mozlila/5.0 (Linux; Android 7.0; SM-G892A ...)"

几个破绽:

一、路径畸形。//images/images/images/images/images/images/cache.php——重复了六次的目录名。正常用户不会访问这种地址,这是扫描器在尝试路径穿越攻击的典型特征。

二、Referer 是伪造的。"www.google.com" 缺少协议头(正常应该是 https://www.google.com/)。这是想伪装成从谷歌点进来的。

三、User-Agent 有明显的拼写错误。Mozlila——"Mozilla"拼错了字母顺序。这是扫描工具的常见特征。

怎么批量发现这类访问

看 UA 里的可疑关键词:

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

这里用了个技巧:-F'"' 把分隔符改成双引号。因为日志里的请求行和 UA 都用双引号包着,用引号切分比空格切分更稳定——UA 字符串中间有空格,用空格切会把它切成好几段

按引号切分后:$2 是请求行,$4 是 Referer,$6 是 User-Agent。这样就能把完整的 UA 字符串取出来了。

找出 404 最多的 IP,往往是扫描器:

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

awk '$9 == 404 {print $1}' 这个写法是 awk 的"模式 + 动作"结构的典型用法:前面 $9 == 404 是过滤条件,只有当条件成立时才执行后面的 {print $1}

这样能直接列出"哪个 IP 制造了最多的 404"——正常的访客不会产生大量 404,扫描器才会。

需要担心吗

不用太紧张。任何暴露在公网的服务器每天都会收到大量扫描——这是常态,不是"被针对"。

扫描器做的事情就是批量试常见的漏洞路径(cache.phpwp-admin.envphpmyadmin 之类),碰运气看有没有没打补丁的系统。只要你的程序本身没有这些漏洞,扫描不会有结果。

但有两件事值得做:

一是定期看 5xx。扫描不会造成 5xx,5xx 说明是你自己的代码出错了。

二是关注异常的 200。如果某个陌生 IP 对同一个 URL 产生了大量 200,可能是有人在爬你的内容。这时候可以在 Nginx 里加访问频率限制。

几个实用参数

统计今天某段时间的访问

awk '/19\/Sep\/2026:1[89]:/ {count++} END {print count}' /var/log/nginx/access.log

用正则匹配时间范围。19[89] 匹配 18 点和 19 点两个小时的记录。

注意斜杠要转义(\/),因为日志里的时间格式是 19/Sep/2026

排除掉自己的访问

自己调试时刷页面会污染统计。把自己的 IP 排除掉

awk '$1 != "你的IP"' /var/log/nginx/access.log | wc -l

这在用日志做统计时是必须的一步——否则你开发一下午,日志里全是自己的记录。

实时看访问

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

-f 是 follow 模式,新写入的日志会实时滚动显示。调试的时候开着这个窗口,另一头访问网站,能立刻看到请求打进来。

这一篇记两个最有用的东西就够了。

一个是 sort | uniq -c | sort -rn 这个套路。不管是统计页面、IP、状态码还是 UA,都是同一套链子换个字段号——记住它就不用背命令了。里面最容易漏的是第一个 sort,少了它 uniq -c 会把同一个值算成好几组,因为相同的行没挨在一起。

另一个是日志视角的独特性。统计插件记的是"正常访问",而日志把扫描器的畸形请求、伪造的 Referer、拼错的 UA 全都原样留着。想知道站点有没有被盯上,只能看日志。

用 awk 的局限也很明显:每次都要重新扫整个文件,日志攒大了会越来越慢,也看不了跨天的趋势。这也是两个工具不能互相替代的原因——临时查一次具体问题用 awk 最快,要做长期统计还是得落库。真正干活的时候,两种方式都会用上。