网站跑起来之后,很多站长只看"有没有人访问",却不看"访问的人是谁、从哪来、看了什么、卡在哪"。Nginx 的 access.log 里其实存着海量的用户行为数据,但原始日志动辄几百 MB,用 grep 翻根本看不过来。GoAccess 就是一款开源的实时 Web 日志分析工具,纯命令行、零数据库依赖,几秒钟就能把几十万行日志解析成可视化的 HTML 报表,非常适合在 VPS 上自建轻量级的访问统计分析。
本文从日志格式定制讲起,一步步完成 GoAccess 的安装、实时分析、GeoIP 归属地解析和自动化报表生成,最后附上常见排错。
一、第一步:定制 Nginx 日志格式
GoAccess 解析日志的前提是"格式对得上"。虽然它内置了 Nginx 的常见格式(combined),但更推荐直接把 access.log 输出成 JSON,这样字段完整、解析零歧义,后续还能直接给其他程序消费。
在 Nginx 的 http 块中定义 JSON 格式:
http {
log_format json_combined escape=json '{'
'"time_local":"$time_local",'
'"remote_addr":"$remote_addr",'
'"remote_user":"$remote_user",'
'"request":"$request",'
'"status":$status,'
'"body_bytes_sent":$body_bytes_sent,'
'"request_time":$request_time,'
'"http_referer":"$http_referer",'
'"http_user_agent":"$http_user_agent",'
'"http_x_forwarded_for":"$http_x_forwarded_for"'
'}';
access_log /var/log/nginx/access.log json_combined;
}
然后重载 Nginx:
nginx -t && nginx -s reload
之后产生的日志形如:
{"time_local":"24/Oct/2026:10:00:00 +0800","remote_addr":"1.2.3.4","request":"GET / HTTP/1.1","status":200,"body_bytes_sent":5210,"request_time":0.013,"http_referer":"-","http_user_agent":"Mozilla/5.0 ..."}
二、安装 GoAccess
Debian/Ubuntu 直接使用官方源安装(自带的旧版本不带 GeoIP,建议走官方源):
# 添加官方仓库
echo "deb https://deb.goaccess.io/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/goaccess.list
wget -O - https://deb.goaccess.io/gnugpg.key | sudo apt-key add -
sudo apt-get update
sudo apt-get install -y goaccess
CentOS/Rocky 使用 yum 源:
cat > /etc/yum.repos.d/goaccess.repo <<'EOF'
[goaccess]
name=GoAccess repo
baseurl=https://repo.goaccess.io/centos/$releasever/$basearch
gpgcheck=1
enabled=1
gpgkey=https://repo.goaccess.io/gnugpg.key
EOF
yum install -y goaccess
验证安装版本(需 >= 1.5 才有较完整的 JSON 支持):
goaccess --version | head -1
# GoAccess - 1.9.3.
三、实时分析与生成 HTML 报表
直接用交互式终端界面实时查看(需要 TTY):
goaccess /var/log/nginx/access.log --log-format=JSON --date-format=%d/%b/%Y --time-format=%T
更适合自动化的是生成静态 HTML 报表:
goaccess /var/log/nginx/access.log \
--log-format=JSON \
--date-format=%d/%b/%Y \
--time-format=%T \
-o /var/www/html/report.html \
--real-time-html \
--port=7890
参数说明:
--log-format=JSON # 日志格式为 JSON
--date-format # 日期格式,与日志中 $time_local 对应
--time-format # 时间格式
-o # 输出 HTML 文件路径
--real-time-html # 开启实时刷新模式(浏览器端轮询)
--port # 实时刷新用的 WebSocket 端口
生成的 report.html 可以直接用 Nginx 托管,浏览器打开 http://你的域名/report.html 即可看到包含访问量趋势、来源国家/地区、热门页面、状态码分布、访客 IP 排行等完整报表。
四、GeoIP 归属地解析
默认安装的 GoAccess 没有地理位置库,报表里"来源地区"会是空的。需要安装 GeoIP 相关依赖:
# Debian/Ubuntu
sudo apt-get install -y libmaxminddb-dev libgeoip-dev geoip-database
# 或 CentOS
yum install -y geoip-devel GeoIP-data
安装时需重新编译 GoAccess 才会带上 GeoIP 支持:
wget https://github.com/allinurl/goaccess/archive/refs/tags/v1.9.3.tar.gz
tar xzf v1.9.3.tar.gz && cd goaccess-1.9.3
./configure --enable-utf8 --enable-geoip=mmdb --with-libmaxminddb
make && sudo make install
使用 MaxMind GeoLite2 免费库(需要注册账号下载,或使用镜像):
# 下载 GeoLite2-City 数据库(示例使用镜像,正式使用请注册 MaxMind 账号)
curl -L -o GeoLite2-City.mmdb https://github.com/P3TERX/GeoLite2-City.mmdb/raw/download/GeoLite2-City.mmdb
sudo mkdir -p /usr/share/GeoIP
sudo mv GeoLite2-City.mmdb /usr/share/GeoIP/GeoLite2-City.mmdb
生成报表时指定数据库:
goaccess /var/log/nginx/access.log \
--log-format=JSON \
--date-format=%d/%b/%Y --time-format=%T \
--geoip-database=/usr/share/GeoIP/GeoLite2-City.mmdb \
-o /var/www/html/report.html
这样报表里就能看到访客来自哪个城市、运营商等信息。
五、自动化:定时生成报表并保护访问
把报表生成写进 cron,每小时更新一次:
# /etc/cron.d/goaccess-report
0 * * * * root /usr/local/bin/goaccess /var/log/nginx/access.log \
--log-format=JSON --date-format=%d/%b/%Y --time-format=%T \
--geoip-database=/usr/share/GeoIP/GeoLite2-City.mmdb \
-o /var/www/html/report.html >/dev/null 2>&1
报表页包含访问者来源等数据,不建议裸奔公开,给 Nginx 加一层 Basic Auth 保护:
server {
listen 80;
server_name stats.example.com;
location / {
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
alias /var/www/html/;
index report.html;
}
}
# 生成账号密码
htpasswd -c /etc/nginx/.htpasswd admin
六、常见排错
报错:Log format not recognized / 解析出的字段为空
90% 是日志格式和 --log-format 对不上。如果 access.log 里还是旧格式的混合日志,先确认新格式已生效:
tail -1 /var/log/nginx/access.log
# 如果末尾还是 "1.2.3.4 - - [24/Oct/...] \"GET ..." 这种 common 格式,
# 说明 access_log 指令没生效,检查是否写在了对的 server 块里并已 reload。
报错:date/time format 不匹配
JSON 日志里 $time_local 形如 24/Oct/2026:10:00:00 +0800,对应 --date-format=%d/%b/%Y --time-format=%T。如果自定义了其他时间格式,必须同步修改这两个参数,否则 GoAccess 会跳过整行日志。
报表里 IP 全是 CDN 节点
和 Nginx 限流场景一样,日志里记录的是 $remote_addr(CDN 节点 IP)。把 JSON 格式里的字段换成真实 IP 即可:
'"remote_addr":"$http_x_forwarded_for"'
(前提是已用 realip 模块或确认 CDN 传了可信的 X-Forwarded-For 头,防止伪造头注入。)
日志轮转后报表出现重复/缺失
logrotate 切出旧日志后,cron 里 goaccess access.log 只解析当前文件,历史数据会丢。可以用 --restore / --persist 增量合并,或把归档日志统一 glob 进去:
goaccess /var/log/nginx/access.log /var/log/nginx/access.log.* \
--log-format=JSON --date-format=%d/%b/%Y --time-format=%T \
-o /var/www/html/report.html
报表生成很慢
第一次解析几百 MB 历史日志属于正常现象。可以先用 --ignore-crawlers 忽略爬虫、用 --anonymize-ip 做 IP 脱敏,后续配合 logrotate 按天切割 + 定时全量重建即可保持速度。
七、总结
GoAccess 的价值在于"零依赖、秒级出报表":把 Nginx 日志改成 JSON 格式后,一条命令就能得到可视化访问分析,配合 GeoIP 库可以看清访客来源,配合 cron 和 Basic Auth 就能搭建一套私有的轻量访问统计服务。它替代不了 Matomo 这类完整埋点分析,但用来快速了解"谁在访问、访问了什么、服务器性能如何",在 VPS 上是性价比极高的选择。日常运维记得先做小流量验证格式,再放开全量解析,避免一次解析卡死服务器。



