2026年11月12日
2026年11月12日
Analyze Nginx Access Logs with GoAccess: Install, Log Format, HTML Reports
This article is a localized edition of the Japanese original. Read the Japanese original
GoAccess is an open-source tool that reads web server access logs and summarizes visitors, the most requested URLs, status codes, referrers, browsers, operating systems and more. You can view it interactively in the terminal, or output a single-file HTML report, JSON or CSV. It needs no tracking tag like Google Analytics and counts the requests that actually reached the server, so you can also see trends in bot and attack traffic.
Using Nginx on Ubuntu as the example, this article covers installing GoAccess, specifying the log format, producing reports and automating daily aggregation. Option names were checked against the official GoAccess manual (goaccess.io/man), and the commands were tested with GoAccess 1.12 (as of October 2026).
Installation
On Ubuntu and Debian, you can install it from the standard repositories.
sudo apt update
sudo apt install goaccess
goaccess --version
The version in the standard repositories can be old. To use a newer version, add the official GoAccess repository (these are the steps from the download page of the official site).
wget -O - https://deb.goaccess.io/gnugpg.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
To build from source, do the following. --enable-geoip=mmdb uses a MaxMind-format (.mmdb) database for country and city lookups, and --with-zlib lets GoAccess read compressed .gz logs directly.
sudo apt install build-essential libncursesw5-dev libmaxminddb-dev zlib1g-dev
wget https://tar.goaccess.io/goaccess-1.12.tar.gz
tar -xzvf goaccess-1.12.tar.gz
cd goaccess-1.12/
./configure --enable-utf8 --enable-geoip=mmdb --with-zlib
make
sudo make install
The only values --enable-geoip accepts are mmdb and legacy (the old GeoIP .dat format).
Specify the Nginx log format
GoAccess cannot aggregate anything unless it knows the format each log line is written in. If you use Nginx's default log format (combined), just pass --log-format=COMBINED. The date and time formats are set automatically.
sudo goaccess /var/log/nginx/access.log --log-format=COMBINED
An aggregation dashboard opens in the terminal. Move between panels with the Tab key and quit with q.
If you log request_time
You may have changed Nginx's log_format to append the processing time ($request_time).
log_format timed '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" "$http_user_agent" $request_time';
access_log /var/log/nginx/access.log timed;
In that case, describe the format to GoAccess field by field. $request_time is in seconds (with millisecond resolution), so map it to %T.
sudo 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
The main specifiers mean the following:
| Specifier | Meaning |
|---|---|
%h |
Client IP address |
%d / %t |
Date / time (formats set with --date-format and --time-format) |
%r |
Request line (method, path, protocol) |
%s |
Status code |
%b |
Response size |
%R |
Referrer |
%u |
User agent |
%T |
Time taken (seconds, with millisecond resolution) |
%^ |
Skip this field |
If the format does not match, "Failed Requests" on the dashboard goes up and most requests are not counted. Try it on a few lines first, confirm the failure count is 0, then use it on your real logs.
Put the settings in a config file
If adding options every time is tedious, write them in a config file. GoAccess reads the per-user ~/.goaccessrc or a system-wide goaccess.conf. The location of goaccess.conf depends on how you installed GoAccess, for example /etc/goaccess/goaccess.conf, or /usr/local/etc/goaccess/goaccess.conf when built from source. Settings are written as the long command-line options without the leading --.
# ~/.goaccessrc
log-format COMBINED
ignore-crawlers true
For a custom format, write it like this:
# ~/.goaccessrc (custom format with request_time)
log-format %h - %^ [%d:%t %^] "%r" %s %b "%R" "%u" %T
date-format %d/%b/%Y
time-format %T
You can also point to a different config file with the -p option (--config-file).
Output reports to a file
Specify an output file with -o, and GoAccess writes an HTML, JSON or CSV report depending on the extension.
# single-file HTML report
sudo goaccess /var/log/nginx/access.log --log-format=COMBINED -o /var/www/stats/report.html
# JSON for processing with other tools
sudo goaccess /var/log/nginx/access.log --log-format=COMBINED -o report.json
You can view the HTML report just by opening it in a browser. Change the report title with --html-report-title.
Common filtering options
| Option | Effect |
|---|---|
--ignore-crawlers |
Excludes crawlers such as search engines from the counts |
--exclude-ip=203.0.113.10 |
Excludes the given IP (ranges such as 203.0.113.0-203.0.113.255 also work) |
--ignore-status=404 |
Excludes the given status code (can be given more than once) |
--no-query-string |
Strips the query string from URLs before counting (merges /?s=test into /) |
--anonymize-ip |
Shows IP addresses with the last part set to 0 |
--keep-last=7 |
Keeps only the last 7 days |
To filter on conditions GoAccess has no option for, select the lines first with grep or similar and pass them through standard input with -.
# aggregate without requests to the admin area (/wp-admin/)
grep -v '"GET /wp-admin/' /var/log/nginx/access.log | goaccess - --log-format=COMBINED -o report.html
Read rotated logs together
On Ubuntu, Nginx logs are split by day by logrotate into access.log.1, access.log.2.gz and so on, and older ones are compressed. You can list several files, and decompress the compressed ones with zcat and pass them through standard input.
# current log and the previous day
sudo goaccess /var/log/nginx/access.log /var/log/nginx/access.log.1 --log-format=COMBINED -o report.html
# everything, including the compressed files
sudo sh -c 'zcat /var/log/nginx/access.log.*.gz | goaccess /var/log/nginx/access.log /var/log/nginx/access.log.1 - --log-format=COMBINED -o report.html'
If your GoAccess was built with --with-zlib, you can also pass .gz files directly as arguments.
Keep aggregated results over time (--persist and --restore)
Logs disappear with rotation, so to keep aggregating over a long period, save GoAccess's aggregated data to disk.
sudo mkdir -p /var/lib/goaccess
# first run: aggregate and save the data
sudo goaccess /var/log/nginx/access.log.1 --log-format=COMBINED \
--persist --db-path=/var/lib/goaccess -o /var/www/stats/report.html
# later runs: load the saved data, add the new log, and save again
sudo goaccess /var/log/nginx/access.log --log-format=COMBINED \
--restore --persist --db-path=/var/lib/goaccess -o /var/www/stats/report.html
Create the directory for --db-path beforehand (GoAccess stops with an error if it does not exist). The default location is /tmp, so specify another location that survives reboots. In our own test, feeding the same log twice in a row did not count already-processed lines twice.
Run this once a day with cron and the report updates automatically every day.
# /etc/cron.d/goaccess (runs every day at 00:10)
10 0 * * * root goaccess /var/log/nginx/access.log.1 --log-format=COMBINED --restore --persist --db-path=/var/lib/goaccess -o /var/www/stats/report.html
Show countries and regions (GeoIP)
To aggregate visitors by country or region, prepare a database (.mmdb format) that maps IP addresses to locations and pass it with --geoip-database. Free options include DB-IP's "IP to City Lite" and MaxMind's GeoLite2 (requires a free account). Both are updated monthly, so replace them regularly.
sudo goaccess /var/log/nginx/access.log --log-format=COMBINED \
--geoip-database=/usr/local/share/GeoIP/dbip-city-lite.mmdb \
-o report.html
Some distribution packages are built without GeoIP support; in that case use the package from the official repository or a source build with --enable-geoip=mmdb.
Real-time HTML report
With --real-time-html, GoAccess starts a WebSocket server, and the HTML report open in your browser updates as lines are appended to the log.
sudo goaccess /var/log/nginx/access.log --log-format=COMBINED \
-o /var/www/stats/report.html --real-time-html
The WebSocket uses port 7890 by default, so open it in your firewall or change it with --port. If you open the report on an HTTPS page, specify a certificate with --ssl-cert and --ssl-key, or set the connection URL with --ws-url. It has to keep running all the time, so if daily numbers are enough, generating a static report with cron as in the previous section is simpler to operate.
Precautions when publishing reports
Reports include visitors' IP addresses and the URLs they requested as is. If you put a report in a public web directory, make sure only the people concerned can see it. With Nginx, add Basic authentication or an IP address restriction.
location /stats/ {
auth_basic "stats";
auth_basic_user_file /etc/nginx/.htpasswd_stats;
allow 203.0.113.0/24;
deny all;
satisfy all;
}
Create /etc/nginx/.htpasswd_stats with htpasswd (in the apache2-utils package on Ubuntu). For reports you share, you can also hide IP addresses with --anonymize-ip.
Summary
- With Nginx's default log format,
--log-format=COMBINEDis all you need. For custom formats, specify each field with specifiers such as%h,%rand%T. - In config files (
~/.goaccessrcand so on), write the long options without the leading--. - Output HTML, JSON or CSV with
-o, and narrow down what is counted with--ignore-crawlersand--exclude-ip. - Read rotated logs by listing several files and using
zcat, and accumulate long-term data with--persist,--restoreand cron. - For GeoIP, pass an .mmdb file with
--geoip-database. Reports contain IP addresses, so restrict who can see them.
The overall Nginx configuration for running WordPress is explained in Configuring Nginx for WordPress.
Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)