GoAccess
goaccess is an open-source real-time web log analyzer and interactive viewer that runs in a terminal or serves live HTML reports via WebSockets. It parses web server logs (Nginx, Apache, Caddy, Cloudflare, AWS CloudFront) fast with minimal CPU/RAM overhead.
Installation
# Debian / Ubuntu (Official repository for latest version)
wget -O - https://deb.goaccess.io/gnupg.key | gpg --dearmor | sudo tee /usr/share/keyrings/goaccess.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/goaccess.gpg arch=$(dpkg --print-architecture)] https://deb.goaccess.io/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/goaccess.list
sudo apt update && sudo apt install goaccess -y
# RHEL / Rocky Linux / AlmaLinux (EPEL)
sudo dnf install epel-release -y
sudo dnf install goaccess -y
# Alpine Linux
apk add goaccess
# Docker
docker run --rm -it -v "/var/log/nginx:/var/log/nginx" allinurl/goaccess:latest \
-f /var/log/nginx/access.log --log-format=COMBINEDQuick Start & Basic Usage
# Interactive TUI mode (prompts for log format if not specified)
goaccess /var/log/nginx/access.log
# Interactive TUI mode with predefined COMBINED format
goaccess /var/log/nginx/access.log --log-format=COMBINED
# Generate a static HTML report
goaccess /var/log/nginx/access.log -o /var/www/html/report.html --log-format=COMBINED
# Generate JSON or CSV report
goaccess /var/log/nginx/access.log -o report.json --log-format=COMBINED
goaccess /var/log/nginx/access.log -o report.csv --log-format=COMBINEDInteractive Terminal UI (TUI) Keybindings
When running goaccess directly in the terminal:
| Key | Action |
|---|---|
F1 / h | Open main help menu |
F5 | Redraw / refresh dashboard |
q | Quit GoAccess or close current expanded module |
0 - 9 | Expand / zoom into module 1 through 10 |
Shift + 0 | Expand module 10 |
Tab / Shift + Tab | Navigate forward / backward through modules |
s | Open sort options dialog for active module |
/ | Search within active module (supports regex) |
n | Jump to next search match |
c | Open / change color scheme palette |
o / Enter | Expand / collapse selected module |
Log Formats & Predefined Presets
GoAccess supports built-in presets via --log-format=<NAME>:
COMBINED: Standard Nginx / Apache combined log format.VCOMBINED: Combined format prefixed with Virtual Host ($host).COMMON: Common Log Format (CLF).CADDY: Caddy web server standard logs.CLOUDFRONT: Amazon CloudFront distributions.CLOUDFLARE: Cloudflare raw log exports.SQUID: Squid proxy native log format.AWS_ALB: AWS Application Load Balancer access logs.
Custom Log Format Specifiers
When your web server uses a custom log_format, match it using these specifiers:
| Specifier | Description |
|---|---|
%h | Remote client host (IP address / hostname) |
%d | Date matching --date-format (e.g. %d/%b/%Y) |
%t | Time matching --time-format (e.g. %H:%M:%S) |
%r | Request line (GET /index.html HTTP/1.1) |
%m | Request method only (GET, POST) |
%U | Requested URL path (without query string) |
%q | Query string (e.g. ?id=123) |
%H | Request protocol (HTTP/1.1, HTTP/2.0) |
%s | HTTP status code (200, 404, 502) |
%b | Size of response in bytes sent to client |
%R | HTTP Referer header |
%u | User-Agent string |
%v | Virtual host name (server_name / $host) |
%T | Request execution time in seconds (with ms resolution) |
%D | Request execution time in microseconds |
%^ | Ignore this field / token |
1. Standard Nginx Combined + Request Time
If your Nginx log has $request_time appended to the end:
# Nginx nginx.conf
log_format custom '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" $request_time';Parse it with 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='%H:%M:%S'2. Cloudflare Proxy Logs (CF-Connecting-IP)
When Nginx sits behind Cloudflare, the first IP is Cloudflare's proxy IP, while the real visitor IP is in $http_cf_connecting_ip:
# Nginx log_format with Cloudflare real IP
log_format cloudflare '$http_cf_connecting_ip - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';Parse using:
goaccess /var/log/nginx/access.log \
--log-format='%h - %^ [%d:%t %^] "%r" %s %b "%R" "%u"' \
--date-format='%d/%b/%Y' \
--time-format='%H:%M:%S'Real-Time HTML Dashboard (WebSockets)
GoAccess can run an embedded WebSocket server that updates the browser dashboard live as incoming log lines are written.
goaccess /var/log/nginx/access.log \
-o /var/www/html/report.html \
--log-format=COMBINED \
--real-time-html \
--ws-url=wss://analytics.example.com:443/ws \
--port=7890Nginx Reverse Proxy for Secure WebSocket (wss://)
Proxy both the HTML report and the WebSocket port (7890) through Nginx with SSL termination:
server {
listen 443 ssl http2;
server_name analytics.example.com;
ssl_certificate /etc/letsencrypt/live/analytics.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/analytics.example.com/privkey.pem;
# Protect dashboard with basic authentication or IP allowlist
auth_basic "Protected Analytics";
auth_basic_user_file /etc/nginx/.htpasswd;
# Serve static HTML report generated by GoAccess
location / {
root /var/www/html;
index report.html;
}
# Proxy WebSocket updates to GoAccess daemon
location /ws {
proxy_pass http://127.0.0.1:7890;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
}Processing Multiple & Compressed Logs
Analyze rotated .gz logs together with the current active log:
# Decompress and feed all rotated logs into GoAccess via pipe
zcat -f /var/log/nginx/access.log* | goaccess - -o /var/www/html/report.html --log-format=COMBINED
# Read multiple uncompressed files directly
goaccess /var/log/nginx/access.log /var/log/nginx/access.log.1 -o report.html --log-format=COMBINED
# Analyze remote logs live via SSH without downloading the file
ssh [email protected] "cat /var/log/nginx/access.log" | goaccess - --log-format=COMBINEDFiltering, Privacy & Performance Tuning
# Exclude office / monitoring IP addresses from metrics
goaccess access.log --exclude-ip=192.168.1.50 --exclude-ip=10.0.0.0/8
# Anonymize client IP addresses (GDPR compliance: masks last octet for IPv4)
goaccess access.log --anonymize-ip --log-format=COMBINED
# Ignore web crawlers, bots, and spiders
goaccess access.log --ignore-crawlers --log-format=COMBINED
# Ignore specific HTTP status codes (e.g. ignore 301/302 redirects)
goaccess access.log --ignore-status=301 --ignore-status=302
# Filter logs by date range before processing
sed -n '/01\/Aug\/2026/,/08\/Sep\/2026/p' access.log | goaccess - --log-format=COMBINED
# Track only specific virtual host from multi-site log
goaccess access.log --log-format=VCOMBINED | grep 'example.com' | goaccess -Data Persistence (On-Disk Storage)
By default, GoAccess holds data in RAM. For large logs over long periods, use on-disk persistence:
# Store incremental data to disk
goaccess access.log --persist --db-path=/var/lib/goaccess/ --log-format=COMBINED
# Restore previous data and append new log lines
goaccess access.log --restore --persist --db-path=/var/lib/goaccess/ --log-format=COMBINED
# Keep only the last N days of data in database
goaccess access.log --persist --restore --db-path=/var/lib/goaccess/ --keep-last=30Systemd Service for Live Background Daemon
Create /etc/systemd/system/goaccess.service to keep real-time reporting running continuously in the background:
[Unit]
Description=GoAccess Real-Time Web Log Analyzer
After=network.target nginx.service
[Service]
Type=simple
User=www-data
Group=www-data
ExecStart=/usr/bin/goaccess /var/log/nginx/access.log \
-o /var/www/html/report.html \
--log-format=COMBINED \
--real-time-html \
--addr=127.0.0.1 \
--port=7890 \
--ws-url=wss://analytics.example.com/ws
Restart=always
RestartSec=5s
[Install]
WantedBy=multi-user.targetEnable and start the service:
sudo systemctl daemon-reload
sudo systemctl enable --now goaccess
sudo systemctl status goaccessAutomated Daily Reports via Cronjob
If you prefer static snapshots rather than running a constant WebSocket daemon:
# Edit crontab
sudo crontab -e
# Generate fresh report daily at midnight
0 0 * * * zcat -f /var/log/nginx/access.log* | goaccess - -o /var/www/html/report.html --log-format=COMBINED >/dev/null 2>&1