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
| Variable | Enables | Validation |
|---|---|---|
NEXT_PUBLIC_POSTHOG_KEY | PostHog | Must start with phc_ |
NEXT_PUBLIC_POSTHOG_HOST | PostHog | Must be a URL |
NEXT_PUBLIC_GA_MEASUREMENT_ID | Google Analytics | Must 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:
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:
| Path | Proxies to |
|---|---|
/ingest/static/:path* | PostHog's static assets |
/ingest/:path* | PostHog's ingestion endpoint |
/ingest/decide | PostHog'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.