--- title: Web Analytics description: Pageviews and traffic measurement, via PostHog and Google Analytics. type: reference related: - /en/docs/packages/analytics/product --- # Web Analytics Two web analytics tools ship wired up, and both are optional: * **PostHog** — the same instance that handles [product analytics](/en/docs/packages/analytics/product), feature flags and error capture. Pageviews come for free once it is initialised. * **Google Analytics 4** — injected through [`@next/third-parties`](https://nextjs.org/docs/app/guides/third-party-libraries), 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](/en/docs/deployment/docker). ## 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`: ```ts title="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: | 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. --- For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md) For an index of all available documentation, see [/llms.txt](/llms.txt)