Skip to content

Commit 0600b15

Browse files
authored
Randomize PostHog proxy path per build (#528)
1 parent a7f2c66 commit 0600b15

3 files changed

Lines changed: 28 additions & 7 deletions

File tree

‎apps/cloud/src/routes/__root.tsx‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,14 @@ if (typeof window !== "undefined" && import.meta.env.VITE_PUBLIC_SENTRY_DSN) {
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‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,28 +106,32 @@ const sentryTunnelMiddleware = createMiddleware({ type: "request" }).server(
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‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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 = () => {
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(

0 commit comments

Comments
 (0)