Adblock Immune Analytics

Ok, this one will be quick. Hey look! My blog has analytics now! And I’m pretty sure if you’re reading this, you didn’t block those analytics with your adblocker. Namely because unless you block all scripts from running, you loaded the scripts from this site. That’s the one trick Doctors Adblockers don’t want you to know: If you host your own analytics on the same domain of your site, its hard to block them! Now lets talk about how to do this.

A Guide to Adblock Immune Analytics (Mainly on Hugo)

I say “Mainly on Hugo” cause this is how I did it. I see no reason why it won’t work on other systems or sites, but this is what I tested it on. I at some point will be trying this with my work in Laravel, and if I do I’ll try to remember to update this.

Vince

Vince Analytics is the key to all of this, its a single docker container that is a drop in replacement for Plausible.io, but it looked a bit easier to self host, and the back end is written in Go, not NodeJS and seems to idle around 40 megabytes of ram on my server. Its neat, if you feel so inclined to follow this guide, go give the Github a star.

The setup

Vince’s one downfall was that the documentation wasn’t super clear. So I’ll be as straight forward as I can. For the purposes of this mini-tutorial, we’re going to assume you’re running a proxy of some kind (my demo will be NGINX) in a container on the same network as the Vince instance. The steps required to get Vince up and running are as follows:

  1. Pull the image and run an admin command in the directory you want to pass through to the container. This is required to setup your admin username and password for the dashboard, and is way easier to do if you just run something like this:
podman run -v /home/vince:/tmp/vince ghcr.io/vinceanalytics/vince admin --name=username --password=1234 --data=/tmp/vince
  1. Now that the admin setup is taken care of, lets go ahead and add the following to whatever container orchestration you use. For purposes of this, I’ll give it to you in docker compose format:
version: '3.8'

services:
  vince-analytics:
    image: ghcr.io/vinceanalytics/vince
    restart: always
    environment:
      VINCE_DATA: tmp/vince/
    volumes:
      - /home/vince:/tmp/vince
    command: serve
  1. NGINX is where the magic happens. Since my site is a Hugo static site, its actually fully hosted in NGINX. So the locations is where this functionality is enabled. Here are the relevant bits to my NGINX config to enable Vince (note, its not the complete config):
location / {
  root /site/;
}
    # Vince path to get the javascript
    location /vince/ {
        proxy_pass http://vince-analytics:8080/;  
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
    # Vince path to have the in browser JS forward to the container's event API
    location /api/event {
        proxy_pass http://vince-analytics:8080/api/event;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
  1. At this point, feel free to plug in your Vince analytics tags to your site! You’re done! My Hugo theme had a 2 line config setup. Which for completeness I’ll share here:
# Integrate Plausible.io
   plausibleDataDomain = 'blog.d6software.com'
   plausibleScriptSource = 'https://blog.d6software.com/vince/js/script.js'

(Since Vince is a drop in replacement for Plausible, anything compatible with Plausible should work, hence it being Plausible.io configs.)

Outro

I hope this helped you get some analytics on your site! And hopefully they’ll be slightly more accurate due to folks not being as easily able to block them. Honestly, seeing how plausible and Vince work, I personally will be excluding them in analytics, cause as a site owner, analytics are wildly useful.

Bonus step 5! Dashboard not on the web via Tailscale

Ok, so the above is how you get it setup, but if you notice, you can’t really login to your dashboard to view your analytics. You could go in and setup a full domain or a different subdomain for that, but I don’t like exposing things when I don’t have to. Enter Tailscale. If you’re unfamiliar, Tailscale is a unique mesh network VPN solution that allows you to route directly to other devices on your network that have a Tailscale connection directly via a wireguard tunnel. They have a wildly generous free tier and the way I think about networks changed once I started using them. I have a second proxy on the server hosting this site, and its a container that is exposed only to my tailnet (for those unfamiliar, that’s what you call the mesh network that Tailscale runs.) Here is a quick docker compose to demo Caddy that is only exposed to my tailnet. For the purposes of this, you also need to know that Tailscale gives you a second Tailnet only IP address, and for the purposes of this demo, that IP is 100.61.226.252

version: '3.8'

services:
  caddy:
    image: caddy:latest
    container_name: caddy
    restart: unless-stopped
    ports:
    #These next two lines are the important ones.
    #Exposing the ports only to your tailnet
      - "100.61.226.252:80:80"
      - "100.61.226.252:443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config

volumes:
  caddy_data:
  caddy_config:

With the above config, this proxy is only on your tailnet, and then I simply proxy the same Vince container instance through this reverse proxy and access it via a subdomain that the reverse proxy manages!

I hope this pattern, if its new to you, is helpful. I have a ton of self hosted services hosted in the cloud that are only exposed to my tailnet this way. Its super neat.