Analytics

Web Analytics

Pageviews and traffic measurement, via PostHog and Google Analytics.

Two web analytics tools ship wired up, and both are optional:

  • PostHog — the same instance that handles product analytics, feature flags and error capture. Pageviews come for free once it is initialised.
  • Google Analytics 4 — injected through @next/third-parties, which loads the tag off the main thread rather than blocking render.

Neither is required. With no keys set, AnalyticsProvider renders its children and nothing else, and initializeAnalytics() returns immediately.

Enabling them

VariableEnablesValidation
NEXT_PUBLIC_POSTHOG_KEYPostHogMust start with phc_
NEXT_PUBLIC_POSTHOG_HOSTPostHogMust be a URL
NEXT_PUBLIC_GA_MEASUREMENT_IDGoogle AnalyticsMust start with G-

PostHog needs both of its variables. One without the other leaves it uninitialised, which is why the schema in packages/analytics/keys.ts checks the prefixes: a key pasted into the wrong variable fails at boot rather than producing a site that silently records nothing.

These are NEXT_PUBLIC_*, so they are inlined into the client bundle at build time. In Docker they are substituted at container start instead — see deployment.

Where it is wired up

AnalyticsProvider wraps each app's root layout and mounts the Google Analytics tag. PostHog is initialised separately, from instrumentation-client.ts:

apps/app/instrumentation-client.ts
import { initializeAnalytics } from '@kreogen/analytics/instrumentation-client';

initializeAnalytics();

Next runs that file before the app hydrates, which is earlier than a provider component can run. It matters for a pageview counter: initialising inside the tree misses the first navigation on a fast client-side route change.

Pageviews behind an ad blocker

Requests to posthog.com are on every blocklist, so a meaningful share of traffic never reports. @kreogen/next-config rewrites them through your own origin instead:

PathProxies to
/ingest/static/:path*PostHog's static assets
/ingest/:path*PostHog's ingestion endpoint
/ingest/decidePostHog's feature flag endpoint

skipTrailingSlashRedirect is set alongside them, because PostHog sends some requests with a trailing slash and Next's default redirect turns those into a 307 the SDK does not follow.

Google Analytics is not proxied. It is blocked at least as often, but the tag is served from Google's own domain and is not reverse-proxyable without breaking consent signals.

Session replay

PostHog can record sessions, including console logs and network errors. It is off by default and enabled in PostHog's own project settings rather than here.

Turn it on deliberately. Replay records what users type, and the recordings land in a third party's storage — so masking rules, and whatever the client's privacy policy says, need deciding before it is switched on rather than after.

What is deliberately absent

There is no Vercel Web Analytics, and no @vercel/analytics dependency anywhere in the repository. It only reports for applications hosted on Vercel, and kreogen deploys as Docker images to wherever the project runs.