Traffic Analytics Service - A web analytics proxy for Ghost that processes and enriches traffic data before forwarding it to Tinybird's analytics API.
The following sequence diagram shows a simplified overview of where the Analytics Service fits in to Ghost's traffic analytics features.
- A user requests a Ghost site's homepage (or any other page on the site's frontend)
- Ghost serves the page's HTML, plus a script called
ghost-stats.js
- The
ghost-stats.js
script executes and sends aPOST
request to the Analytics Service'sPOST /tb/web_analytics
endpoint - The Analytics Service receives the request and processes it. This includes parsing the user agent, generating a user signature, etc.
- The Analytics Service proxies the request to Tinybird
- Tinybird receives the request and stores it in its Clickhouse database
- The Analytics Service then proxies the response from Tinybird back to the user's browser.
sequenceDiagram
autonumber
participant User as User Browser
participant Ghost as Ghost Site
participant AS as Analytics Service
participant TB as Tinybird
User->>Ghost: GET /
activate Ghost
Ghost-->>User: HTML + ghost-stats.js
deactivate Ghost
Note over User: ghost-stats.js executes
User->>+AS: POST /tb/web_analytics
AS->>AS: Process Request
AS->>+TB: POST /v0/events<br/>Enriched analytics data
TB-->>-AS: 202 Accepted
AS-->>-User: 202 Accepted
Note over User,TB: Analytics event successfully tracked
- User agent parsing for OS, browser, and device detection
- Referrer URL parsing and categorization
- Privacy-preserving user signatures with daily-rotating salts
Copy .env.example
to .env
and configure as needed. For local development with Ghost, see Develop locally with Ghost
Pre-requisites:
- A container runtime, such as Docker Desktop or Orbstack
- Docker Compose
git clone
this repo &cd
into it as usualyarn dev
to build & start all required development services. The Analytics Service will be reachable athttp://localhost:3000
.
It is possible to run this Analytics Service locally alongside your local development instance of Ghost. This can be useful to test the full end-to-end flow from Ghost > ghost-stats.js
> Analytics Service > Tinybird and back.
Here are the steps to get setup:
- In the Ghost repo, run
yarn tb
to run Tinybird local and deploy the Tinybird schema defined in Ghost. - Now we need to retrieve some values from our tinybird-local container. :
workspaceId
: runtb info
in your Tinybird shell, and copy theworkspace_id
valueadminToken
: runtb token ls
and copy the token that is named "admin token"trackerToken
: runtb token ls
and copy the token that is named "tracker"
- Update your Ghost configuration in
ghost/core/config.local.json
orghost/core/config.local.jsonc
with the following. Make sure to replace the${workspaceId}
and${adminToken}
with the values from step 2:
{
"tinybird": {
"workspaceId": "${workspaceId}",
"adminToken": "${adminToken}",
"tracker": {
"endpoint": "http://localhost:3000/tb/web_analytics",
"datasource": "analytics_events"
},
"stats": {
"endpoint": "http://localhost:7181"
}
}
}
- Ensure you've set the
trafficAnalytics
labs flag in Ghost. This can be done through the Admin UI in settings, or more simply via config:
{
"labs": {
"trafficAnalytics": true
}
}
- Add the following to your
.env
file in the root of this repo. Make sure to replace${trackerToken}
with the value from step 2:
TINYBIRD_TRACKER_TOKEN=${trackerToken} # From step 2
PROXY_TARGET=http://host.docker.internal:7181/v0/events # The URL of your Tinybird-local container
- Start the Analytics Service with
yarn dev
from the root of this repo. - Start Ghost with
yarn dev
in a new terminal window from the root of your Ghost repo.
To confirm everything is working:
- Load your local Ghost site's frontend in your browser at
http://localhost:2368
- In your network tab, you should see a successful POST request to
/tb/web_analytics
- In your network tab, you should see a successful POST request to
- Load Ghost admin in your browser at
http://localhost:2368/ghost
and navigate to the Analytics tab in the sidebar- You should see a "Unique Visitor" recorded in the main Overview tab
yarn test:types
— run Typescript typechecks in Dockeryarn test:unit
— run all unit tests in Dockeryarn test:integration
— run all integration tests in Dockeryarn test
— run typechecks, unit tests and integration tests in Dockeryarn test:e2e
— run e2e tests (with wiremock) in Docker
yarn lint
run eslint in docker compose
This project supports running multiple worktrees simultaneously using Docker Compose. Each worktree can run its own isolated development environment with unique ports and container names.
- Create worktrees as usual with git worktree
- Configure each worktree with a unique
.env
file:
# main worktree (.env) - uses defaults
NODE_ENV=development
# work worktree (.env)
NODE_ENV=development
COMPOSE_PROJECT_NAME=traffic-analytics-work
ANALYTICS_PORT=3001
FIRESTORE_PORT=8081
# scratch worktree (.env)
NODE_ENV=development
COMPOSE_PROJECT_NAME=traffic-analytics-scratch
ANALYTICS_PORT=3002
FIRESTORE_PORT=8082
Each worktree runs completely isolated:
- Unique ports: No conflicts between worktrees
- Isolated containers: Auto-generated names like
traffic-analytics-work-analytics-service-1
- Separate volumes: Each worktree has its own
node_modules
volume - Independent projects: Services can run simultaneously
# Start development in any worktree
cd /path/to/worktree
docker compose up
# Each worktree accessible on its configured port
# main: http://localhost:3000
# work: http://localhost:3001
# scratch: http://localhost:3002
Merging to main
automatically deploys to staging. Production deployments only happen when the version in package.json
changes.
To ship to production:
yarn ship
- creates a release branch with version bump- Create PR using the provided link
- Merge the PR - this triggers production deployment
Copyright (c) 2013-2025 Ghost Foundation - Released under the MIT license.