Counterscale is a simple web analytics tracker and dashboard that you self-host on Cloudflare.
It's designed to be easy to deploy and maintain, and should cost you near-zero to operate – even at high levels of traffic (Cloudflare's free tier could hypothetically support up to 100k hits/day).
NOTE: Counterscale is currently in very early development and shouldn't be used in any actual production setting. We welcome people trying it and giving feedback/contributing, but heads up this project is still super early.
If you don't have one already, create a Cloudflare account here.
- Go to your Cloudflare dashboard and set up a Cloudflare Workers subdomain
- Enable Cloudflare Analytics Engine beta for your account (screenshot)
- Create a Cloudflare API token. This token needs
Account.Account Analytics
permissions at a minimum. - Run
npm install
- Run
npx wrangler secret put CF_BEARER_TOKEN
→ when prompted, paste the API token you created - Run
npx wrangler secret put CF_ACCOUNT_ID
→ when prompted, paste your Cloudflare Account ID - Run
npm run deploy
– this will do two things:- Create a new worker,
counterscale
, now visible under Workers and Pages in Cloudflare - Create a new Analytics Engine dataset, called
metricsDataset
- Create a new worker,
- It should now be live. Visit
https://counterscale.{yoursubdomain}.workers.dev
.
If the website is not immediately available (e.g. "Secure Connection Failed"), it could be because Cloudflare has not yet activated your subdomain (yoursubdomain.workers.dev). This process can take a minute; you can check in on the progress by visiting the newly created worker in your Cloudflare dashboard (Workers & Pages → counterscale).
The deployment URL can always be changed to go behind a custom domain you own. More here.
When Counterscale is deployed, it makes tracker.js
available at the URL you deployed to:
https://counterscale.{yoursubdomain}.workers.dev/tracker.js
To start tracking website traffic on your web property, copy/paste the following snippet into your website HTML:
<script>
(function () {
window.counterscale = {
q: [["set", "siteId", "your-unique-site-id"], ["trackPageview"]],
};
})();
</script>
<script
id="counterscale-script"
src="https://counterscale.{yoursubdomain}.workers.dev/tracker.js"
defer
></script>
Be sure to replace your-unique-site-id
with a unique string/slug representing your web property. Use a unique site ID for each property you place the tracking script on.
To get started, in the project root, copy .dev.vars.example
to .dev.vars
.
Open .dev.vars
and enter the same values for CF_BEARER_TOKEN
and CF_ACCOUNT_ID
you used earlier.
Counterscale is built on Remix and Cloudflare Workers. In development, you'll run two servers:
- The Remix development server
- The Miniflare server (local environment for Cloudflare Workers)
You run both using:
npm run dev
Afterwards, the dashboard should be visible at http://127.0.0.1:8787.
If you want to check the production build, you can stop the dev server and run following commands:
npm run build
npm start
Then refresh the same URL in your browser (no live reload for production builds).
There is only one "database": the Cloudflare Analytics Engine dataset, which is communicated entirely over HTTP using Cloudflare's API.
Right now there is no local "test" database. This means in local development:
- Writes will no-op (no hits will be recorded)
- Reads will be read from the production Analaytics Engine dataset (local development shows production data)
The primary goal of Counterscale is to be super easy to self-host and maintain. It should be "set up once and forget".
To achieve that:
- There should be no application state outside of CF Analytics Engine
- e.g. no additional relational database like MySQL, PostgreSQL, etc.
- That means no
users
table, nosites
table, etc. - This also means retention will be limited by what CF Analytics Engine provides. While it could be possible to stand up a "hit counter" for long-lived data (e.g. years), that would mean another database, which we will not pursue.
- We prioritize backwards compatibility
- New
metricsDataset
columns can be added, but old columns cannot be removed or renamed (they can however, be "forgotten"). - That also means it's okay if a feature only works during a period where the data is active.
- New