· All Posts · All in OpenBSD · All in Web

Static Site Generator for Web Analytics Dashboards

With Google Analytics, you allow Big Tech to monetize your visitors. Privacy and minimalism doesn’t mix well with tracking, privacy intrusions and bloated Javascript clients. Server side analytics utilize only log files for basic insights into visitors and hits. GoAccess is one such tool that does log file parsing and deals with the presentation of the data that we use in the following.

Unlike the name suggests, GoAccess isn’t written in Go, but C. The tradeoff with log parsing is that we don’t get nearly as much information as Google Analytics-style system. There’s no screen sizes, no time-on-page metrics, etc, but it doesn’t require any Javascript, no cookie or GDPR notice and is sufficient for most use cases.

GoAccess static HTML for

The setup consists of an OpenBSD httpd web server, a Tor Onion hidden service and the Gemini protocol gmifs server, from previous articles. All of these produce logs which we can combine in a single, statically generated HTML dashboard that we serve publicly under /stats/. That’s right, with everything else being open on this web log I do publish my web analytics with anonymized IPs, except for crawlers, publicly.

httpd logs

Ensure you have set the log style to combined, an extension to Common Log Format with referrer and user-agent fields for our primary www server.

server "" {
    log style combined

Log the hidden service access logs into a custom file, naturally we won’t get IP address or referrers from Tor.

server "noximhkcqevri46e2kuthman5o6emplfcevppx3hvsvu55qcygj5elyd.onion" {
    log access onion.log

Gemini gmifs logs are under /var/www/logs/gemini/access.log. We don’t get http status codes but can ingest hits for Gemini. For this I now log with a prefix to get the hostname right.


Install GoAccess

pkg_add goaccess

Edit /etc/goaccess/goaccess.conf for global configuration. The primary www web server has these settings:

# httpd(8) combined log format
date-format %d/%b/%Y
time-format %T %z
log-format %v %h %^ %^ [%d:%t] "%r" %s %b "%R" "%u"
# privacy for visitors identities
anonymize-ip true
# keep stats between runs
persist true
restore true
# ignore some panels I don't need
ignore-panel VISIT_TIMES
ignore-panel VIRTUAL_HOSTS
ignore-panel STATUS_CODES

I copy the file to /etc/goaccess/goaccess.gmi.conf and /etc/goaccess/goaccess.onion.conf.

For the hidden service I’m using the same combined log format above. For gmifs@1.0.2 I’m using the common log format and exclude the status code:

# httpd(8) common log format
date-format %d/%b/%Y
time-format %T %z
log-format %v %h %^ %^ [%d:%t] "%r" %^ %b

All other options will be set with command line arguments.

Stats Database

We will keep all stats under /var/db/goaccess/

mkdir -p /var/db/goaccess/
chown daemon /var/db/goaccess/

Script and Periodic Updates

I copy the following script to /usr/local/bin/ Note that I’m excluding the IP of the box that runs this blog as well as localhost for the hidden service hits. I’m also excluding my static exit IPs from my WireGuard VPN, not listed here.

doas vi /usr/local/bin/
doas chmod +x /usr/local/bin/

/usr/local/bin/goaccess $1 \
  -p $2 \
  -o /var/www/htdocs/ \
  --exclude-ip \
  --exclude-ip \
  --db-path=/var/db/goaccess/ \
  --html-report-title=" stats"

And run it with an @hourly cron for the www, the hidden service and the gemini server:

# vi /var/cron/tabs/root
0 * * * * /usr/local/bin/ /var/www/logs/access.log /etc/goaccess/goaccess.conf > /dev/null 2>&1
0 * * * * /usr/local/bin/ /var/www/logs/onion.log /etc/goaccess/goaccess.onion.conf > /dev/null 2>&1
0 * * * * /usr/local/bin/ /var/www/logs/gemini/access.log /etc/goaccess/goaccess.gmi.conf > /dev/null 2>&1

In order to ensure we generate the report before log rotations executes, prefix the entries in /etc/newsyslog.conf:

/var/www/logs/access.log			644  4     *    $W0   Z "/usr/local/bin/ /var/www/logs/access.log /etc/goaccess/goaccess.conf && pkill -USR1 -u root -U root -x httpd"
/var/www/logs/gemini/access.log		644  4     *    $W0   Z "/usr/local/bin/ /var/www/logs/gemini/access.log /etc/goaccess/goaccess.gmi.conf && pkill -USR1 -u root -U root -x gmifs"

Boom. We now get quite exhaustive web stats statically generated at /stats/.

Published on Sunday, Dec 26, 2021. Last modified on Monday, Sep 11, 2023.
Go back

If you’d like to support me, follow me on Twitter or buy me a coffee. Use Bitcoin
BTC address: bc1q6zjzekdjhp44aws36hdavzc5hhf9p9xnx9j7cv