IndieStats.

Privacy-friendly analytics on your own infrastructure.

Self-hosted Laravel analytics: real-time dashboards, advanced filters, and localized exports — multiple sites, no cookies, no mandatory consent banners.

Features

Everything you need in one place — without handing visitor data to third parties or weighing down the sites you track.

Pageviews & context
localStorage visitor IDs (no cookies), time on page, document.title, query string, language, timezone, search terms, and bot detection.
Real-time
Active visitors, last-5-minute pageviews, a 30-minute activity chart, and a live feed — on the dashboard and per site.
Nine analytics tabs
Summary, Real-time, Content, Traffic, UTM, Technology, Geography, Visitor, and Events — each with server-side DataTables.
Advanced filters
Tom Select type-ahead across source, path, title, UTM, device, country, ASN, visitor, and more — applied to every tab and export.
Geography & ASN
Choropleth country map via MaxMind GeoLite2. ISP / hosting provider via DB-IP ASN Lite.
Events & outbound
Custom events with up to 20 properties, goals, and clicks to external domains.
Localized export
Background XLSX jobs with sheet titles and headers in IT, EN, DE, FR, or ES.
Multi-site
Multiple sites per account, domain allowlists, Cloudflare-aware client IP, noscript pixel, and scheduled pruning.

Screenshots

From the multi-site overview down to the snippet you paste on your pages.

IndieStats multi-site dashboard with trend charts and unique visitors
Multi-site overview — unique visitors and trends for every site at a glance.
Per-site analytics with summary metrics and daily trend chart
Per-site detail — summary metrics, filters, and the daily pageviews trend.
Site configuration with allowed domains and tracking snippet
Sites & snippets — add a site, set allowed domains, and copy the tracking code.

Install

One command. PHP 8.3+, Composer, and Node 20+. SQLite by default — MySQL and PostgreSQL work too.

git clone https://github.com/andreapollastri/indiestats.git
cd indiestats
composer run setup

The setup script installs dependencies, runs the Vite build, creates .env, generates the app key, and runs migrations.

Manual install

composer install
npm install
npm run build
cp .env.example .env
php artisan key:generate
touch database/database.sqlite
php artisan migrate

Quick start

From zero to your first metrics in three steps.

01

Install & run

Clone the repo, run composer run setup, then start the stack with composer run dev.

02

Create a site & paste the snippet

Add a site with a name and allowed domains. Copy the async script and noscript fallback before the closing body tag — the base URL is your APP_URL.

03

Open the dashboard

Multi-site overview with live visitor counts, then nine per-site tabs — all narrowed by the same filters and exportable to localized Excel.

Optional demo data

Add SEED_FAKE_DATA=true to .env, then:

php artisan db:seed

Creates an admin user (admin@users.test / password) and, when seeding is enabled, sample analytics.

Tracking

A lightweight async snippet plus a 1×1 pixel for visitors without JavaScript.

<script async src="https://your-indiestats-url/i/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.js"></script>
<noscript><img src="https://your-indiestats-url/collect/pixel.gif?k=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&p=/" width="1" height="1" /></noscript>

The script tracks pageviews, time on page, outbound clicks, UTM parameters, on-site search queries, page title, query string, language, timezone, and a per-tab session ID.

Custom events

indiestats.track('signup');

indiestats.track('purchase', {
  plan: 'pro',
  price: '29.99',
  currency: 'USD'
});

Up to 20 key–value properties per event. Tie events to goals in the dashboard to watch conversions.

Allowlist: only domains listed on the site can send data (validated via Origin / Referer). Always set allowed domains in production.

Dashboard

A multi-site overview, then dedicated tabs for every angle of traffic.

  • Overview — unique visitors, pageviews, sparklines, live counts, and date ranges (today → 1 year).
  • Summary — headline metrics, top pages, and top sources.
  • Real-time — active visitors, last 5 minutes, 30-minute chart, live activity feed.
  • Content / Traffic / UTM — paths, titles, referrers, search queries, and campaigns.
  • Technology — device, browser, OS, language, timezone, and ASN.
  • Geography — country table plus interactive choropleth map.
  • Visitor / Events — visitor IDs, human vs bot, custom events and goals.

UI is localized in IT, EN, DE, FR, and ES — including DataTables, country names, and export sheets.

Filters

One filter bar. Every tab, chart, realtime count, DataTables AJAX call, and Excel export respects it.

Filter Description
SourceReferrer / engine
Search termOn-site search query
Page / Page titlePath and document.title
Query stringFull landing-page query
UTM (5)source, medium, campaign, term, content
EventCustom event name
Device / Browser / OSFrom User-Agent
Language / TimezoneFrom the tracker script
Visitor / typePersistent ID · human vs bot
Country / ASNGeoIP and network

Production

Deploy without surprises. Set APP_URL to HTTPS, build assets, run the scheduler and a queue worker.

npm run build
php artisan config:cache
php artisan route:cache
php artisan view:cache
Variable Notes
APP_URL Required. Public HTTPS URL used by the tracker snippet.
APP_ENV / APP_DEBUG production / false
QUEUE_CONNECTION Needed for Excel exports (default database)
ANALYTICS_RETENTION_DAYS Raw data pruning window (default 375)
GEOIP_* Optional MaxMind / ASN database paths and license key
Proxies: behind Cloudflare, CF-Connecting-IP is used for GeoIP and ASN. For other reverse proxies, configure Laravel trusted proxies.

Scheduler

One cron entry drives pruning, weekly GeoIP updates, and monthly ASN downloads.

* * * * * cd /path-to-indiestats && php artisan schedule:run >> /dev/null 2>&1
Command Schedule Purpose
analytics:prune Daily 02:00 Remove raw pageviews, events, and outbound clicks past retention
geoip:update Mondays 04:15 Download GeoLite2-Country when a MaxMind key is set
dbip-asn:update 3rd of month 04:30 Download DB-IP ASN Lite

Queue

Excel exports run as background jobs. Locally, composer run dev covers the worker. In production, keep one running:

php artisan queue:work --sleep=3 --tries=3

Use Supervisor (or equivalent) so the worker restarts automatically.

Deploy with Cipi

Run IndieStats on any Ubuntu VPS with cipi.sh — PHP-FPM, MariaDB, Nginx, Let's Encrypt, zero-downtime releases, and queue workers.

Explore cipi.sh →