Repository navigation
fix(analytics): capture posthog events only on the production host - #576
Merged
Merged
Conversation
PostHog received events from localhost, 127.0.0.1, the 8787 preview and workers.dev previews: the SDK was enabled on every build and the consent layer opted in by default. Off the production host (siteUrl from the production runtime config, so dev and preview builds have none) a base plugin opts PostHog out, covering pageview, autocapture, pageleave and recording. The consent controller never opts in there, including from a stored choice. Production behaviour is unchanged. Claude-Session: https://claude.ai/code/session_01Px57iMQNzBFqJ4Y9aZf8Dt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
PostHog (EU project) receives pageviews from non-production origins, for example
http://localhost:8787/en/bluemap. Theposthogblock innuxt.config.tsships the SDK on every build, and the consent layer opts in by default. Sopnpm dev,pnpm preview,pnpm preview:prodand the CI jobs inseo.yml(Lighthouse,seo:check,cache-check, the concurrency check against localhost:8787) all send real events. The CI jobs are the likely source of most of that traffic.Design
The gate compares
window.location.hostnamewith the production host. That host comes from the production-onlysiteUrlruntime config (the$productionblock), so there is no second copy of the domain, and dev and preview builds have no production host at all.layers/base/plugins/posthog-production-gate.client.ts(dependsOn: ['posthog']): on any other host it calls$clientPosthog.opt_out_capturing(). posthog-js checks the opt-out at capture time, so this covers the router's$pageview, autocapture, pageleave and session recording.layers/consent/utils/consentController.ts: a newanalyticsHostport makesapplyAnalytics(allowed && analyticsHost). An accepted or stored opt-in never switches capture on off production. Decisions are still stored and the banner still closes.layers/base/utils/analyticsHost.ts: the pure helpersproductionHostnameandisAnalyticsHost.Why not the alternatives:
posthog.disabledin nuxt-posthog needs a build-time value. The same production bundle is served on localhost:8787, so the host is only known in the browser.clientOptionsalone cannot stop therouter.afterEachcapture or a consent opt-in.Known limit: the banner still appears on localhost, and a choice made there is stored but has no effect. posthog-js still calls
/flagson localhost; that is a feature-flag lookup, not an event.Tests
tests/analytics/analytics-host.spec.ts: the production host matches; localhost, 127.0.0.1,localhost:8787, a workers.dev alias, and an empty or missing host do not; the production hostname is read from the site URL.tests/consent/controller.spec.ts: off the production host, accepting or restoring a stored opt-in never opts PostHog in, and the decision is still stored.Locally:
pnpm test190 files / 1786 tests passing;pnpm qualityno regression;pnpm typecheckshows only the three pre-existing errors;pnpm buildpasses.Verified in a headless browser
Built with
NODE_ENV=production pnpm build, served withpnpm preview:prod. Playwright (Chromium) loaded/en/bluemap, clicked a link and a button, then accepted the cookie banner.http://localhost:8787: zero capture requests (no event, batch or snapshot calls). PostHog reports opted out, and still opted out after accepting the banner.--host-resolver-rules="MAP onelitefeather.net 127.0.0.1"onhttp://onelitefeather.net:8787: capture requests are sent, and accepting the banner opts in, so production behaviour is unchanged.https://claude.ai/code/session_01Px57iMQNzBFqJ4Y9aZf8Dt