History

Add PostHog analytics to marketing site

Mirrors the cloud app's setup: client init in Layout.astro behind PUBLIC_POSTHOG_KEY, plus an Astro middleware that reverse-proxies /api/${PUBLIC_ANALYTICS_PATH} to us.i.posthog.com and us-assets.i.posthog.com so events flow past adblockers.

Rhys Sullivan committed 6 months ago

Commit 40353be1 parent 63cdca34 files · +50 −0Browse files

Unified · Split

4 files
  1. apps/marketing/package.json +1 −0
  2. apps/marketing/src/layouts/Layout.astro +16 −0
  3. apps/marketing/src/middleware.ts +32 −0
  4. bun.lock +1 −0
apps/marketing/package.json+1−0
@@ -22,6 +22,7 @@ · 21 unmodified lines
2222 "@tailwindcss/vite": "^4.2.2",
2323 "astro": "^6.1.3",
2424 "effect": "catalog:",
25+ "posthog-js": "^1.372.5",
2526 "tailwindcss": "^4.2.2"
2627 },
2728 "devDependencies": {
apps/marketing/src/layouts/Layout.astro+16−0
@@ -32,6 +32,22 @@ const { · 31 unmodified lines
3232 <body class="bg-surface text-ink antialiased selection:bg-teal/20 selection:text-teal">
3333 <slot />
3434
35+ <script>
36+ import posthog from 'posthog-js'
37+
38+ const key = import.meta.env.PUBLIC_POSTHOG_KEY
39+ if (key) {
40+ const analyticsPath = (import.meta.env.PUBLIC_ANALYTICS_PATH ?? 'a').replace(/^\/+|\/+$/g, '')
41+ posthog.init(key, {
42+ api_host:
43+ import.meta.env.PUBLIC_POSTHOG_HOST ?? `${window.location.origin}/api/${analyticsPath}`,
44+ ui_host: 'https://us.posthog.com',
45+ defaults: '2025-05-24',
46+ person_profiles: 'identified_only',
47+ })
48+ }
49+ </script>
50+
3551 <script>
3652 // Scroll-triggered reveals
3753 const observer = new IntersectionObserver(
apps/marketing/src/middleware.ts+32−0
@@ -0,0 +1,32 @@
1+import { defineMiddleware } from "astro:middleware";
2+
3+// PostHog reverse proxy — the browser SDK targets a build-randomized
4+// first-party path and we forward to PostHog's ingest + asset hosts. Keeps
5+// events flowing past adblockers that match *.posthog.com. See
6+// https://posthog.com/docs/advanced/proxy/cloudflare
7+
8+const POSTHOG_INGEST_HOST = "us.i.posthog.com";
9+const POSTHOG_ASSETS_HOST = "us-assets.i.posthog.com";
10+const POSTHOG_PROXY_PATH = `/api/${(import.meta.env.PUBLIC_ANALYTICS_PATH ?? "a").replace(
11+ /^\/+|\/+$/g,
12+ "",
13+)}`;
14+
15+export const onRequest = defineMiddleware(async (context, next) => {
16+ const { pathname } = new URL(context.request.url);
17+ if (pathname !== POSTHOG_PROXY_PATH && !pathname.startsWith(`${POSTHOG_PROXY_PATH}/`)) {
18+ return next();
19+ }
20+
21+ const url = new URL(context.request.url);
22+ url.hostname = pathname.startsWith(`${POSTHOG_PROXY_PATH}/static/`)
23+ ? POSTHOG_ASSETS_HOST
24+ : POSTHOG_INGEST_HOST;
25+ url.protocol = "https:";
26+ url.port = "";
27+ url.pathname = pathname.slice(POSTHOG_PROXY_PATH.length) || "/";
28+
29+ const upstream = new Request(url, context.request);
30+ upstream.headers.delete("cookie");
31+ return fetch(upstream);
32+});
bun.lock+1−0
@@ -185,6 +185,7 @@ · 184 unmodified lines
185185 "@tailwindcss/vite": "^4.2.2",
186186 "astro": "^6.1.3",
187187 "effect": "catalog:",
188+ "posthog-js": "^1.372.5",
188189 "tailwindcss": "^4.2.2",
189190 },
190191 "devDependencies": {