用 GoAccess 实时看懂 Nginx 日志:哪些 IP 在爬、哪个页面最慢,一眼看穿

服务器上的 Nginx 每天都在写 access.log,可大多数站长从来没正经看过它。这个文件里藏着网站几乎所有的事实:谁在访问、从哪来、看什么页面、什么时段流量最高、有没有人在恶意扫描、搜索引擎爬虫到底来没来。问题在于,access.log 是几千上万行的纯文本,肉眼根本看不出规律。

GoAccess 就是干这个的。它把 Nginx 日志实时解析成终端里的可视化面板,安装简单、资源占用极低,个人 VPS 上跑起来毫无压力。这篇文章从安装、配置、解析 Nginx 日志格式,讲到怎么用实时面板快速判断"今天的流量正常不正常",全部是我在自己服务器上的实际操作。

一、为什么选 GoAccess 而不是 AWStats

老牌方案 AWStats 是每天定时跑批生成静态报表,看的是昨天的数据,配置也啰嗦。GoAccess 的优势是快和实时:直接读日志文件,终端里就能实时刷新,不需要 web 服务器生成 HTML,也不需要等定时任务。它占用的资源很小,解析百万行日志也就几秒钟。

安装用系统包管理器最省事:

apt install goaccess
# 或者 CentOS / Rocky
dnf install goaccess

装完先看版本,确认支持实时模式:

goaccess --version

版本低于 1.0 的功能比较旧,Debian 12 和 Ubuntu 22.04 以上仓库里的版本都够用。如果你的发行版仓库版本太老,GoAccess 官方源提供最新版,但一般来说没必要折腾。

二、告诉 GoAccess 你的日志长什么样

这一步是最容易出错的地方,也是最关键的。GoAccess 需要知道日志里每个字段的位置和格式,判断错了就会出现"总请求数对不上""时间显示异常"这类问题。Nginx 默认的 combined 格式长这样:

$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"

对应的 GoAccess 格式串是:

--log-format=COMBINED

如果你的 Nginx 配置里自定义过 log_format,就得手写格式串。假设你的配置是这样:

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" '
                '"$http_user_agent" "$http_x_forwarded_for" '
                '$request_time';

那么 GoAccess 参数就是:

--log-format='%h %^[%d:%t %^] "%r" %s %b "%R" "%u" "%^" %T'

格式串的对应关系要记牢:%h 是主机名(IP),%d 是日期,%t 是时间,%r 是请求行,%s 是状态码,%b 是响应字节,%R 是 referer,%u 是 user agent,%T 是请求耗时(秒)。%^ 表示跳过这个字段。最后的 %T 让 GoAccess 能统计"最慢的请求",这个很有用。

手写格式串容易错,有个验证技巧:先跑 tail -1 access.log 看一行原始日志,逐字段对照格式串,确认每个引号、空格都对得上。GoAccess 对格式很敏感,多一个空格都会解析失败或者数错。

三、用实时面板看流量全貌

最常用的命令是实时终端面板:

goaccess /var/log/nginx/access.log --log-format=COMBINED -c

-c 表示启用配置对话框,第一次跑会问你日志格式,回答完它会记到 /etc/goaccess/goaccess.conf 里,下次就不用再选。进入面板后,你看到的是分区块的统计:

顶部是总览——总请求数、独立访客数、流量大小、被请求的文件数。这里要重点看两个比例:一是独立访客和总请求数的比,如果总请求数远大于访客数(比如 100 倍以上),说明有大量重复请求,可能是爬虫、也可能是有人在刷;二是带宽消耗,如果某个时段流量异常高但访客数不多,要警惕是不是被恶意下载或者图片被外链盗用了。

往下是访问最多的 URL(Requested Files)、访问最多的 IP、404 和 5xx 统计、User Agent、Referer。每个区块都能用键盘操作:按 Tab 或数字键切换区块,方向键上下选择,回车展开详情,s 键排序。最实用的是在 IP 区块按 s,按请求数从高到低排序,第一名如果是某个陌生 IP 且请求数上千,基本可以确定是爬虫或者扫描器。

四、不用交互,直接生成可读报表

实时面板适合看一眼,但如果你想把数据留下来、或者发给别人看,可以用非交互模式输出成 HTML:

goaccess /var/log/nginx/access.log   --log-format=COMBINED   --ignore-crawlers   -o /var/www/html/report.html   --html-report-title="站点流量报表"

几个参数值得说明。--ignore-crawlers 会把识别出来的搜索引擎爬虫排除掉,这样统计出来的"访客"更接近真实用户。注意这个参数的取舍:如果你就是想知道爬虫来了多少次,那就不要加它。--html-report-title 设置报表标题,会显示在页面顶部。

如果要分析多天的日志,直接用 shell 通配符把所有文件喂进去:

goaccess /var/log/nginx/access.log* --log-format=COMBINED -o report.html

注意 logrotate 压缩后的 .gz 文件 GoAccess 也能直接读,这点比很多分析工具方便。但如果日志跨了多个文件,要确认格式串一致——如果中途改过 log_format,老日志的字段数量和新日志不同,混在一起解析会出错,最好分开分析。

五、真正有价值的几个判读场景

工具会用不难,难的是看懂数据说明什么。分享几个我实际遇到的场景:

第一个场景:404 页面暴增。看 404 区块,如果大量 404 集中在同一路径模式,比如都是 /wp-login.php 或者 /.env,说明有人在批量扫描已知漏洞。这类扫描不用慌,只要你的服务器没跑 WordPress,返回 404 就是正确响应。但如果 404 集中在你自己站内的链接上,那就是站内死链,得修——搜索引擎爬到死链会浪费抓取配额,也会影响用户体验。修复方法是用 GoAccess 定位具体 URL,然后去 Nginx 里配 301 跳转或者修正页面内链。

第二个场景:某个 IP 请求数排名第一且稳定在每秒几次。这通常是采集爬虫。判断方法是看它的 User Agent:如果 UA 是空白的、或者伪装成 Chrome 但请求间隔完全均匀(真实用户不可能这么规律),基本可以确定。应对方式不是立刻封 IP(可能误伤 NAT 后的真实用户),而是先在 Nginx 里对该 UA 限速,观察几天再说:

limit_req_zone $binary_remote_addr zone=perip:10m rate=5r/s;
limit_req zone=perip burst=10 nodelay;

第三个场景:Referer 里出现陌生域名,且那个域名的访问量很高。这说明有人在盗用你的图片或资源。打开 Referer 区块排序,找到来源域名,然后给它配置防盗链(这是另一个话题,核心是 valid_referers 校验)。

第四个场景:访问时段分布。GoAccess 会按小时显示请求分布,如果发现凌晨 3 点到 5 点有异常高峰,而你的网站用户群体主要在白天活动,那这个高峰很可能是爬虫或者定时扫描。结合 IP 区块一起看,能快速定位是谁在半夜爬你的站。

六、把实时监控变成日常习惯

配置一次,日常用起来很简单:想知道当下有没有异常,直接开实时面板,扫一眼 IP 排行和 404 区块,十秒钟就有判断。想留档,就加个 cron 每天生成 HTML 报表,存到独立目录里,出问题时可以对比历史数据。

如果要留档,建议按日期归档,方便日后对比:

goaccess /var/log/nginx/access.log --log-format=COMBINED   -o /var/www/html/reports/$(date +%F).html

配合 cron 每天凌晨生成前一天的报表,手上有几周的连续数据之后,你就能看出网站的"正常基线"是什么样——平时的独立访客大概多少、爬虫占比多少、404 大概几条。有了基线,任何偏离都能第一时间察觉,这比任何告警规则都管用。

还有一个容易被忽略的细节:默认情况下 Nginx 不会记录请求耗时。如果你的 log_format 里没有 $request_time,GoAccess 的"最慢请求"区块就是空的。加上它,然后在 GoAccess 参数里指定 %T,你就能看到哪些接口真的慢。对个人站长来说,比起装一堆 APM 监控,直接在日志里看请求耗时是最轻量的方案:

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" '
                '"$http_user_agent" rt=$request_time';

注意加了自定义字段后,GoAccess 的格式串也要同步更新,末尾补上 rt=%T 对应的部分。改完记得 nginx -t 测试再 reload,别把线上配置弄挂了。

更进一步,可以把 GoAccess 的告警能力用起来。它支持通过 --real-time-html 输出实时网页,也支持在特定条件下触发脚本。不过对个人站长来说,最实用的还是养成"每天看一眼"的习惯:这一眼里能发现 80% 的问题——被爬、被扫、死链、盗链,都是靠日志看出来的。服务器运维很大一部分工作,本质就是持续地看懂日志。GoAccess 把这件事的门槛降到了几乎为零,值得每个站长花半小时配置好。

Last modification:September 27th, 2026 at 01:28 pm

Leave a Comment