History

Randomize PostHog proxy path per build (#528)

Rhys Sullivan committed 6 months ago

Commit 0600b151 parent a7f2c663 files · +28 −7Browse files

Unified · Split

3 files
  1. apps/cloud/src/routes/__root.tsx +7 −1
  2. apps/cloud/src/start.ts +9 −5
  3. apps/cloud/vite.config.ts +12 −1
apps/cloud/src/routes/__root.tsx+7−1
@@ -26,8 +26,14 @@ if (typeof window !== "undefined" && import.meta.env.VITE_PUBLIC_SENTRY_DSN) { · 25 unmodified lines
2626 }
2727
2828 if (typeof window !== "undefined" && import.meta.env.VITE_PUBLIC_POSTHOG_KEY) {
29+ const analyticsPath = (import.meta.env.VITE_PUBLIC_ANALYTICS_PATH ?? "a").replace(
30+ /^\/+|\/+$/g,
31+ "",
32+ );
33+
2934 posthog.init(import.meta.env.VITE_PUBLIC_POSTHOG_KEY, {
30− api_host: import.meta.env.VITE_PUBLIC_POSTHOG_HOST ?? `${window.location.origin}/ingest`,
35+ api_host:
36+ import.meta.env.VITE_PUBLIC_POSTHOG_HOST ?? `${window.location.origin}/api/${analyticsPath}`,
3137 ui_host: "https://us.posthog.com",
3238 defaults: "2025-05-24",
3339 person_profiles: "identified_only",
apps/cloud/src/start.ts+9−5
@@ -106,28 +106,32 @@ const sentryTunnelMiddleware = createMiddleware({ type: "request" }).server( · 105 unmodified lines
106106 );
107107
108108 // ---------------------------------------------------------------------------
109−// PostHog reverse proxy — the browser SDK targets `/ingest/*` (configured in
110−// routes/__root.tsx) and we forward to PostHog's ingest + asset hosts. Keeps
109+// PostHog reverse proxy — the browser SDK targets a build-randomized
110+// first-party path and we forward to PostHog's ingest + asset hosts. Keeps
111111 // events flowing past adblockers that match *.posthog.com. See
112112 // https://posthog.com/docs/advanced/proxy/cloudflare
113113 // ---------------------------------------------------------------------------
114114
115115 const POSTHOG_INGEST_HOST = "us.i.posthog.com";
116116 const POSTHOG_ASSETS_HOST = "us-assets.i.posthog.com";
117+const POSTHOG_PROXY_PATH = `/api/${(import.meta.env.VITE_PUBLIC_ANALYTICS_PATH ?? "a").replace(
118+ /^\/+|\/+$/g,
119+ "",
120+)}`;
117121
118122 const posthogProxyMiddleware = createMiddleware({ type: "request" }).server(
119123 ({ pathname, request, next }) => {
120− if (pathname !== "/ingest" && !pathname.startsWith("/ingest/")) {
124+ if (pathname !== POSTHOG_PROXY_PATH && !pathname.startsWith(`${POSTHOG_PROXY_PATH}/`)) {
121125 return next();
122126 }
123127
124128 const url = new URL(request.url);
125− url.hostname = pathname.startsWith("/ingest/static/")
129+ url.hostname = pathname.startsWith(`${POSTHOG_PROXY_PATH}/static/`)
126130 ? POSTHOG_ASSETS_HOST
127131 : POSTHOG_INGEST_HOST;
128132 url.protocol = "https:";
129133 url.port = "";
130− url.pathname = pathname.replace(/^\/ingest/, "") || "/";
134+ url.pathname = pathname.slice(POSTHOG_PROXY_PATH.length) || "/";
131135
132136 const upstream = new Request(url, request);
133137 upstream.headers.delete("cookie");
apps/cloud/vite.config.ts+12−1
@@ -1,3 +1,4 @@
1+import { randomUUID } from "node:crypto";
12 import { fileURLToPath } from "node:url";
23 import { defineConfig, loadEnv, type Plugin } from "vite";
34 import { cloudflare } from "@cloudflare/vite-plugin";
@@ -44,9 +45,19 @@ const loadWranglerPublicVars = () => { · 40 unmodified lines
4445 );
4546 };
4647
48+let generatedAnalyticsPath: string | undefined;
49+const getGeneratedAnalyticsPath = () => {
50+ generatedAnalyticsPath ??= randomUUID().slice(0, 8);
51+ return generatedAnalyticsPath;
52+};
53+
4754 export default defineConfig(({ mode }) => {
4855 const env = loadEnv(mode, process.cwd(), "");
49− const publicEnv = { ...loadWranglerPublicVars(), ...env };
56+ const publicEnv = {
57+ ...loadWranglerPublicVars(),
58+ VITE_PUBLIC_ANALYTICS_PATH: getGeneratedAnalyticsPath(),
59+ ...env,
60+ };
5061
5162 return {
5263 define: Object.fromEntries(