Skip to content
New storyWe built Skald to count visitors. It caught an AI-key thief.
SkaldEvery visit, truly told.

set-up · apache · with or without Docker

Apache log analytics from the combined log you already keep

Apache log analytics with no new module and, for most sites, no change to Apache. One command finds your site's CustomLog in Apache's settings and sends each new line to your Skald.

Apache log analytics is reading the access log Apache writes for every request, and turning it into visits, sources and pages. Skald reads Apache's standard combined LogFormat: the address, the time, the request, the status, the size, the referrer and the user agent.

30-day free trial · no card · we set it up within one working day · free to self-host

root@web-server: ~ · sample run
$ curl -fsSL https://<your Skald>/traffic-kit/<code> | sudo bash
Skald: installing the log shipper for example.com.

== 1. How it runs here
   with docker (Docker works here)

== 2. The access log of example.com
   found in the web server's settings: /var/log/apache2/example.com-access.log

Done. The shipper for example.com runs with docker and sends to
your Skald.

Sample output, shortened. ${APACHE_LOG_DIR} in your CustomLog line is read as /var/log/apache2.

[01] the log format

Apache's combined LogFormat, one token at a time

This is the line Apache ships with. Skald reads it as it is. Click a token to see what Apache writes there, and what Skald does with it.

apache2.conf · LogFormat … combined

click a token

"%{Referer}i": where they came from

What Apache writes

The page the visitor came from, when the browser says, such as "https://chatgpt.com/".

What Skald does with it

The source of the visit: an AI assistant, a search engine, a campaign or Direct. Many apps strip it, so AI clicks are a floor.

[02] one command

How the one command finds your Apache CustomLog

You do not tell it where the log is. It reads Apache's settings the way Apache does, and picks the virtual host of your site.

  1. It reads /etc/apache2/sites-enabled/ on Debian and Ubuntu, and /etc/httpd/conf.d/ and httpd.conf on the Red Hat family.
  2. It finds the virtual host whose ServerName or ServerAlias is your site, or www. your site.
  3. It takes that host's CustomLog, with ${APACHE_LOG_DIR} read as /var/log/apache2.
  4. Several logs: it asks, the biggest first. None: it tries /var/log/apache2/access.log and /var/log/httpd/access_log.
  5. It runs the shipper with Docker if Docker works, or else as a systemd service with Python 3.9 or newer.
root@web-server: ~ · the command, as a template
$ curl -fsSL https://<your Skald>/traffic-kit/<code> | sudo bash

Your Skald shows the real line in Settings > Connections. The code works once, for 24 hours.

name the log yourself · template
$ curl -fsSL https://<your Skald>/traffic-kit/<code> | sudo bash -s -- --log /var/log/apache2/example.com-access.log

--yes asks nothing. --dry-run shows every step and changes nothing.

[03] one field, if you run Next.js

An Apache LogFormat for a Next.js site

A click inside a Next.js site sends no HTML request, and a link prefetch looks the same in a plain log. One field at the end of the LogFormat tells them apart.

Add this at the very end, inside the LogFormat quotes:

the field to add
 rsc_prefetch=%{Next-Router-Prefetch}i

Then test and reload. On Debian and Ubuntu:

root@web-server: ~
$ sudo apachectl configtest && sudo systemctl reload apache2

On the Red Hat family the service is httpd. Apache writes - when the header was not sent. Any other site can skip this.

/var/log/apache2/example.com-access.log · sample4 lines
  1. 09:14:02 203.0.113.24 "GET /pricing/" 200 "GPTBot/1.2"AI · training
  2. 09:14:03 198.51.100.7 "GET /" 200 "chatgpt.com" Safaripeople · from ChatGPT
  3. 09:14:04 198.51.100.7 "GET /docs/?_rsc=1x" 200 rsc_prefetch=-a click: page view
  4. 09:14:04 198.51.100.7 "GET /pricing/?_rsc=2y" 200 rsc_prefetch=1a prefetch: not a page

Sample lines. With the field, a click inside the site is a page view and a prefetch never is.

[04] check and undo

Check the Apache shipper, or take it away

The check reads only. Each line says OK, or PROBLEM with the fix under it, or CANNOT TELL.

  • The log must be readable by group adm. If not, the check prints the fix: sudo chgrp adm and sudo chmod 640 on the file, and the same group and mode in logrotate's create line.
  • One log per site. On Debian, a virtual host with no CustomLog writes to other_vhosts_access.log, shared by other sites. The check says when a log is shared.
  • Never lost, never twice. The shipper saves its place only after Skald has a batch, and numbered batches are never counted twice.
root@web-server: ~
$ sudo skald-ship-check
✓ OK  the web server (apache2) is running
✓ OK  the access log /var/log/apache2/example.com-access.log is there and group adm may read it
✓ OK  the access log is growing (written in the last 10 minutes)
✓ OK  www.example.com writes to /var/log/apache2/example.com-access.log
✓ OK  the send address answers (401 without a token is expected)
✓ OK  skald-ship is running (0 restarts)
root@web-server: ~ · undo
$ sudo skald-ship-uninstall

Sample output, shortened.

[05] honest limits

What Apache log analytics with Skald does not do

What it does not do

  • It does not edit Apache. The Next.js field is yours to add; the check only says when it is missing.
  • It does not read Apache's common format, which has no referrer and no user agent. Use combined.
  • It has no country for an Apache line unless a GeoIP country file is set on the Skald server. Without one, the country says Not recorded.
  • It does not record the request's size for Security on Apache. That optional field is documented for nginx only.
  • It sees only requests that reach Apache. Behind a CDN, cached pages need the CDN's own log or the Skald Worker.
  • It sends lines written after the install. Older lines in the file are not sent.

questions

Apache log analytics FAQ

Straight answers, including what Skald does not do.

Which Apache LogFormat does Skald read?

Apache's combined format: LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined. It holds everything Skald needs. Apache's common format has no referrer and no user agent, and Skald does not read it: switch the CustomLog line to combined.

Do I need to install an Apache module?

No. Skald reads the log file Apache already writes, and the shipper runs beside Apache, not inside it. The only change some sites make is one field at the end of the LogFormat, for a Next.js site.

My virtual host has no CustomLog line. What happens?

On Debian and Ubuntu, a virtual host with no CustomLog of its own writes to other_vhosts_access.log, which every such site shares. Give the site its own CustomLog, so its lines are not mixed with other sites. sudo skald-ship-check says when a log is shared with other host names.

Does it work on Red Hat, Rocky or AlmaLinux?

The one command reads httpd's settings too: /etc/httpd/conf/httpd.conf and /etc/httpd/conf.d/. A relative path such as logs/access_log is read from /etc/httpd. If it finds no log, it tries /var/log/httpd/access_log, and --log names any other file.

Does it need Docker?

No. It uses Docker when Docker works on the server, or else a systemd service with Python 3.9 or newer. If neither is there, it stops and says what to install.

Will a request be lost or counted twice?

No. The shipper saves its place only after Skald has taken a batch, and batches are numbered so Skald drops one it already has. It follows logrotate's renames and copytruncate.

$ tail -f access.log | skald

See what AI does with your site. Free for 30 days.

No card. We set up hosted Skald for you within one working day, or you self-host it free. Every line of your log, sorted and added up.

Start free trial Live demo