为什么个人站长也该看访问日志
很多草根站长的运维习惯是:网站能打开、Google 能搜到、Cloudflare 面板上有几个数字,就算完事了。但真正决定你站点命运的东西,全藏在服务器本地的 access.log 里——哪些页面被爬虫反复抓、哪个 IP 在偷偷扫你的后台、哪几个 URL 天天 499、哪些静态资源 404 到爆。这些信息你在 GA4 或百度统计里根本看不到,因为统计脚本只对真实执行了 JS 的浏览器生效,而爬虫、扫描器、被墙的请求压根不跑你的 JS。
问题在于,access.log 是给人看的纯文本,一天几万行,用 grep 硬看效率极低。今天这篇讲一个我用了三年、几乎零维护成本的方案:GoAccess——一个用 C 写的单文件日志分析器,不依赖数据库,几十 MB 内存就能把 Nginx/Apache 日志变成实时刷新的 HTML 仪表盘。
GoAccess 到底解决什么问题
先讲清楚它和 Google Analytics 的分工,避免你期望错位:
- GA4 看的是人:真实浏览器、转化路径、来源渠道。它依赖 JS 和 cookie,会被广告拦截插件屏蔽,且数据在 Google 手里。
- GoAccess 看的是请求:每一个打到服务器的 HTTP 请求,包括爬虫、扫描器、API 调用、静态资源。数据完全在你自己的机器上。
两者不是替代关系。我通常用 GA4 看内容效果,用 GoAccess 看服务器健康度和安全态势。尤其是「SSH 被爆破、后台被扫」这类事情,GoAccess 第一时间就能在「访客 IP」和「请求的 URL」面板里看到异常聚集。
安装:一条命令搞定
Debian/Ubuntu 直接装官方源版本就够了。注意一点:官方仓库里的 GoAccess 默认不带某些解析器(比如 GeoIP、OpenSSL 支持),如果你要按国家/地区统计,得用 GoAccess 官方仓库的包。生产环境我建议直接用官方仓库:
# Debian/Ubuntu 官方仓库版本(够用,简单)
apt-get update
apt-get install -y goaccess
# 验证版本,确认是否编译了 geoip/ssl 支持
goaccess --version
# 输出里会列出 "Optional libraries" 和已启用项
如果你看到 --version 里没有 GeoIP,但你又想按国家统计,两个选择:要么加官方 apt 源,要么干脆用 GeoIP 的姊妹项目 geoip-database 配合。个人站大陆访客为主的话,其实可以跳过 GeoIP,把精力放在 URL 和 IP 统计上,性价比更高。
Nginx 日志格式必须先改
这是整篇文章最关键、也是最多人踩坑的一步。GoAccess 能不能发挥全部实力,取决于你的 log_format 里有没有足够字段。Nginx 默认的 combined 格式只有 IP、时间、请求、状态码、referer、UA,缺少响应时间、请求耗时等关键指标。
在 /etc/nginx/nginx.conf 的 http { } 块里加一个自定义格式:
log_format main_goaccess '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time '
'"$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main_goaccess;
这里我特意加了三个字段:
$request_time:Nginx 从收到客户端第一个字节到发出最后一个字节的总耗时(秒,含小数)。$upstream_response_time:后端(PHP-FPM/Node)处理耗时。两者相减,就知道时间是耗在 Nginx 还是耗在后端。$http_x_forwarded_for:如果你前面挂了 Cloudflare 或反代,真实 IP 在 XFF 头里。注意别被伪造的 XFF 骗了——配合set_real_ip_from只信任 Cloudflare 的 IP 段。
改完 nginx -t 测试,再 systemctl reload nginx。新日志格式从 reload 那一刻开始生效,旧日志不受影响。
生成静态 HTML 报告(推荐日常用法)
GoAccess 最省心的用法是「跑一次、生成一个 HTML 文件」,然后用浏览器打开看。它会把 CSS/JS 全部内联进单个 HTML,可以离线打开、可以丢到任意目录:
goaccess /var/log/nginx/access.log \
--log-format=COMBINED \
--output=/var/www/html/report.html \
--html-report-title="zz1984 访问统计"
但注意:上面命令里的 --log-format=COMBINED 是错的,因为你前面改了日志格式,字段更多了。必须用自定义格式字符串,字段顺序和 log_format 严格一一对应:
goaccess /var/log/nginx/access.log \
--log-format='%h - %^ [%d:%t %^] "%r" %s %b "%R" "%u" %T %^ "%^"' \
--date-format=%d/%b/%Y \
--time-format=%T \
--output=/var/www/html/report.html
这里的占位符含义:%h=远程 IP,%^=忽略该字段,%d:%t=日期时间,%r=请求行,%s=状态码,%b=响应字节,%R=referer,%u=UA,%T=请求耗时。为什么用 %^ 把 XFF 和 upstream 时间忽略了?因为 GoAccess 的核心面板用不到它们,多解析反而拖慢速度。
踩坑提醒:如果你的日期格式用的是 [$time_local](即 05/Oct/2026:13:20:01 +0800),那 --date-format 必须是 %d/%b/%Y,--time-format 是 %T。写错一个字母,GoAccess 会报「Token 'xxx' doesn't match specifier」或者干脆统计出 0 条。日志格式和解析器不匹配,是新手最常见的失败原因。
实时 HTML 模式:挂后台自己刷新
日常我更爱用实时模式,让 GoAccess 常驻,浏览器页面每几秒自动刷新:
goaccess /var/log/nginx/access.log \
--log-format='%h - %^ [%d:%t %^] "%r" %s %b "%R" "%u" %T %^ "%^"' \
--date-format=%d/%b/%Y --time-format=%T \
--real-time-html \
--output=/var/www/html/report.html \
--daemonize \
--port=7890
这里 --real-time-html 会在本地起一个 7890 的 WebSocket,浏览器加载 report.html 后通过它拉增量数据。关键坑:WebSocket 的地址默认是当前主机名 + 7890 端口。如果你的站点走 HTTPS(大概率是),浏览器会拦截 ws:// 混合内容并报错,报告就永远停在初始状态。两个解法:
- 用 Nginx 反代 7890 端口,把
/ws路径升级成wss://; - 或者干脆不用实时模式,改用下面的「定时生成」方案,最省事。
定时生成 + Nginx 鉴权,安全又自动
实时模式虽酷,但暴露一个无鉴权的 HTML 报告到公网,等于把你的 IP 分布、后台路径、错误 URL 全送给扫描器。我的做法是定时生成 + Basic Auth 保护。
先加个 cron,每小时重生成一次报告:
# /etc/cron.d/goaccess
0 * * * * root /usr/bin/goaccess /var/log/nginx/access.log \
--log-format='%h - %^ [%d:%t %^] "%r" %s %b "%R" "%u" %T %^ "%^"' \
--date-format=%d/%b/%Y --time-format=%T \
--ignore-crawlers --output=/var/www/html/report.html 2>/dev/null
再用 Nginx 把报告目录藏起来,加 Basic Auth:
location /report.html {
root /var/www/html;
auth_basic "restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
allow 127.0.0.1;
deny all;
}
生成 htpasswd:htpasswd -c /etc/nginx/.htpasswd admin(没装 apache2-utils 就先 apt install apache2-utils)。
几个必看面板与实战读法
报告里我每次最先看三块:
- Requested Files(请求的文件):如果
/wp-login.php、/xmlrpc.php、/.env、/phpmyadmin这类路径冲上榜单,说明你正在被扫描。个人站不用这些的,直接在 Nginx 里 444 掉。 - 404 URLs:长期 404 的内链说明你站内有死链,既浪费爬虫预算又伤体验。顺手修掉,是免费 SEO 优化。
- Visitors → IP 分布:单个 IP 请求数远超正常用户的,直接扔进 fail2ban 或 Cloudflare 拦截。
还有一个隐藏功能:--ignore-crawlers 参数可以把已知爬虫从统计里剔除,让「真实访客」数据更干净。上面 cron 里我已经加上了。如果你反而想看爬虫行为(比如判断百度蜘蛛来没来),就把它去掉。
性能与资源占用
很多人担心分析大日志会吃满 CPU/内存。实测:一个 200MB、约 150 万行的 access.log,GoAccess 单线程跑完大约 10~20 秒,峰值内存 100MB 以内。它对个人站完全无压力。日志特别大时,先用 logrotate 切分,只分析当天的文件即可:
goaccess /var/log/nginx/access.log.1 --log-format=... --output=...
另外提醒,GoAccess 默认会把整个文件读进内存建哈希表。如果你日志累积到几个 GB,记得用 --keep-db-files 或先 zcat 分片,别一次性喂进去。
小结
GoAccess 是那种「装一次、用三年」的工具。它不解决炫酷问题,但让你从「凭感觉运营」变成「看着数据运维」:爬虫是否正常、后台是否被扫、死链有多少、后端慢不慢,一个 HTML 报告全交代清楚。核心就三步——改 Nginx 日志格式、对齐 GoAccess 解析格式、挂 cron 定时生成加鉴权。三步做到位,你就拥有了一套完全自主、不依赖第三方、零成本的访问分析体系。
个人站长资源有限,选择工具的标准应该是「学习成本低、维护成本低、数据在自己手里」。GoAccess 三条全部满足,值得进你的标准运维工具箱。