Search

Search IconIcon to open search

GoatCounter

Last updatedUpdated: by Simon Späti · CreatedCreated: · 2 min read

Easy web analytics. No tracking of personal data.

GoatCounter is an open-source web analytics platform available as a free donation-supported hosted service or self-hosted app. It aims to offer easy-to-use and meaningful privacy-friendly web analytics as an alternative to Google Analytics, Matomo, or Umami (Web Analytics).

More on Why GoatCounter was made.

# How Visits are counted

GoatCounter ==only counts “visits” rather than “pageviews”==:

  • A “pageview” is every time a page is loaded.
  • A “visit” is the first time someone loads a page. Someone reloading the page or going to another page and then coming back is counted as one visit.

You almost always want to keep track of visits rather than pageviews. Otherwise someone reloading the page ten times will show up as ten times, which is not really meaningful.

This can be disabled in the site settings, at Settings → Data collection → Sessions. If it’s disabled every pageview counts as a “visit”.

# Technical details

The way visitors are identified is as follows:

  1. A sessionID is created as concat(siteID, User-Agent, IP).
  2. Store this in memory as a sessionID → UUIDv4 mapping for 8 hours.
  3. Store a UUID → seen_paths mapping (again in memory), so we can count new visits for different paths.
  4. Use the UUID in the database and such.

The IP address and User-Agent are never stored to the database or disk, and there is no conceivable way to trace the random UUID back to this.

It’s only stored in memory, which is needed anyway for basic networking to work.

See more at GoatCounter documentation

# Pseudocode

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
session_key    = site_id + user_agent + IP
count_as_visit = false

# We've seen this session before.
if sessions[session_key] and sessions[session_key].newer_than(8_hours)
    # Only count as visit if this session hasn't visited this path yet.
    if not sessions[session_key].seen_path(current_path)
        count_as_visit = true
        add_current_path(sessions[session_key])
    end
else
    # Generate new session.
    sessions[session_key] = create_random_uuid()
    add_current_path(sessions[session_key])
    count_as_visit = true
end

# Store pageview; only the random UUID in sessions[session_key] is stored,
# and NOT session_key
store_pageview()

# Increate counter to make the charts go up.
if count_as_visit
    increase_counter_in_database()
end

Origin:
References: GitHub repo