Skip to Content
DeploymentAnalytics

Analytics

Classifyre ships optional integrations for two independent analytics providers:

ProviderPurposeConfigured by
PostHogProduct analytics — events, funnels, session behaviourHelm (frontend.posthog.*)
Google Analytics 4Audience and traffic reporting via gtag.jsHelm (frontend.googleAnalytics.*)

Both are off by default, can be enabled independently, and coexist without interfering — enabling one does not affect the other.

No data leaves the browser unless you explicitly enable a provider and supply its ID.

How the web app resolves analytics IDs

The web container image is built once in CI and configured per-deployment by Helm. That rules out NEXT_PUBLIC_* variables for this purpose: Next.js inlines those into the JavaScript bundle at build time, so anything Helm sets afterwards is invisible to the browser.

Instead the web container serves the browser-facing analytics config at runtime:

Browser → GET /classifyre-cfg/ → web container reads env → JS assigning window.__CLASSIFYRE_ANALYTICS__
  • The root layout loads /classifyre-cfg/ synchronously, before React hydrates.
  • The response is Cache-Control: no-store, so a helm upgrade takes effect on the next page load.
  • When Google Analytics is enabled the same response also carries the standard gtag.js bootstrap and injects the Google tag.
  • Changing a token or measurement ID needs no image rebuild — only a helm upgrade.

Like /classifyre-usr, the path name is deliberately neutral: paths containing analytics, tracking, or gtag are targeted by ad-blocker filter lists.

This applies to the web app only. The docs site and blog are static exports built by their own pipeline — see Docs site and blog below.

PostHog

How it works

All PostHog traffic is proxied through the web container at /classifyre-usr:

Browser → /classifyre-usr/* → web container → us.i.posthog.com
  • Requests from the browser go to your own domain first, never directly to posthog.com.
  • Ad-blockers that block known analytics domains are bypassed.
  • No extra ingress rules or firewall exceptions are needed.

The server-side proxy route reads POSTHOG_INGEST_HOST to determine the upstream endpoint. This env var is never exposed to the browser, so you can safely set it to your PostHog region or a managed reverse proxy CNAME.

Getting a project token

Create a PostHog account

Sign up at posthog.com (cloud) or self-host PostHog on your own infrastructure.

Create a project

In the PostHog dashboard, go to Settings → Projects → New project and give it a name (e.g. classifyre-production).

Copy the project token

Navigate to Settings → Project → Project API key. The token looks like phc_xxxxxxxxxxxxxxxxxxxxxxxxxxxx.

Enabling via Helm

frontend:
  posthog:
    enabled: true
    token: "phc_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"   # your PostHog project token
    # host defaults to /classifyre-usr (built-in proxy) — no change needed

Configuration reference

ValueDefaultDescription
frontend.posthog.enabledfalseSet to true to inject PostHog env vars into the web container.
frontend.posthog.token""PostHog project token (phc_...). Required when enabled=true.
frontend.posthog.host"/classifyre-usr"Browser-facing ingest path (NEXT_PUBLIC_POSTHOG_HOST). Keep the default to route through the built-in proxy.
frontend.posthog.uiHost"https://us.posthog.com"PostHog UI host for toolbar links (NEXT_PUBLIC_POSTHOG_UI_HOST). Use https://eu.posthog.com for EU Cloud.
frontend.posthog.ingestHost"https://us.i.posthog.com"Server-side upstream endpoint (POSTHOG_INGEST_HOST). Never exposed to the browser. Use https://eu.i.posthog.com for EU Cloud, or your managed reverse proxy CNAME.

EU Cloud

Set both the UI host and the server-side ingest host for EU Cloud:

frontend:
  posthog:
    enabled: true
    token: "phc_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    uiHost: "https://eu.posthog.com"
    ingestHost: "https://eu.i.posthog.com"

For even higher event capture rates, set up a PostHog managed reverse proxy on your own subdomain (e.g. e.yourcompany.com). Point ingestHost at the CNAME PostHog provisions for you, and set host to the same subdomain so the browser sends events there directly — bypassing the Next.js proxy hop entirely.

frontend:
  posthog:
    enabled: true
    token: "phc_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    host: "https://e.yourcompany.com"
    uiHost: "https://us.posthog.com"
    ingestHost: "https://e.yourcompany.com"   # same: proxy handles its own upstream

Choose a subdomain that does not contain words like analytics, tracking, telemetry, or posthog — ad-blockers target these terms.

Google Analytics

Getting a measurement ID

Create a GA4 property

In Google Analytics, go to Admin → Create → Property and complete the setup.

Add a web data stream

Under Admin → Data streams → Add stream → Web, enter the site URL.

Copy the measurement ID

The stream details show a Measurement ID of the form G-XXXXXXXXXX.

Enabling via Helm

frontend:
  googleAnalytics:
    enabled: true
    measurementId: "G-XXXXXXXXXX"

The chart fails the render with an explicit error if enabled: true is set without a measurement ID, so a misconfiguration cannot reach the cluster silently.

Configuration reference

ValueDefaultDescription
frontend.googleAnalytics.enabledfalseSet to true to inject the Google Analytics measurement ID into the web container.
frontend.googleAnalytics.measurementId""GA4 measurement ID (G-XXXXXXXXXX), injected as GOOGLE_ANALYTICS_MEASUREMENT_ID. Required when enabled=true.

The value is validated against ^G-[A-Z0-9]{4,20}$ before being served to the browser; anything else is ignored and no Google tag is loaded.

The web app is a single-page app, so most navigations do not reload the page. GA4 counts these through Enhanced measurement → Page changes based on browser history events, which is on by default. If you turned Enhanced measurement off, only the first page of each visit is counted.

Ad-blockers

Unlike PostHog, Google Analytics is not proxied: gtag.js is loaded directly from googletagmanager.com and hits go to google-analytics.com, both of which are blocked by most ad-blockers and by some corporate DNS. Expect Google Analytics to under-report relative to PostHog. Use PostHog for numbers you intend to rely on.

Docs site and blog

The docs site and blog are static exports deployed separately from the Helm chart (for example on Cloudflare Pages), so their configuration is baked in at build time rather than injected by Helm. Set these variables in the build environment:

VariablePurpose
NEXT_PUBLIC_GA_MEASUREMENT_IDGA4 measurement ID (G-XXXXXXXXXX). Leave unset to disable.
NEXT_PUBLIC_POSTHOG_PROJECT_TOKENPostHog project token. Leave unset to disable.
NEXT_PUBLIC_POSTHOG_HOSTPostHog ingest host, e.g. https://eu.i.posthog.com.
NEXT_PUBLIC_POSTHOG_UI_HOSTPostHog UI host, e.g. https://eu.posthog.com.

See apps/docs/.env.example and apps/blog/.env.example. Because these are build-time values, changing them requires a rebuild and redeploy of the affected site.

Disabling analytics

Set enabled: false (the default) for either provider. No env vars are injected, the corresponding SDK never initialises, and no requests are made to any analytics endpoint.

frontend:
  posthog:
    enabled: false
  googleAnalytics:
    enabled: false

For the docs site and blog, leave the corresponding NEXT_PUBLIC_* variables unset at build time.

Last updated on