diff --git a/proxy.ts b/proxy.ts index 1d81165..118018a 100644 --- a/proxy.ts +++ b/proxy.ts @@ -41,6 +41,19 @@ function sentryIngestOrigin(): string | null { } } +// Origin serving the Umami analytics script (script.js) and receiving its event +// beacons (POST /api/send). Mirrors the component default so the CSP allows both +// loading the script AND sending events; stays in sync with NEXT_PUBLIC_UMAMI_SRC +// when overridden. +function umamiOrigin(): string { + const src = process.env.NEXT_PUBLIC_UMAMI_SRC || "https://fickanalytics.phluit.net/script.js" + try { + return new URL(src).origin + } catch { + return "" + } +} + // Build the Content-Security-Policy. `script-src` uses 'unsafe-inline' because // Next.js 16's Turbopack build does NOT stamp a per-request nonce onto its // inline hydration scripts (`self.__next_f.push(...)`). A nonce-based policy @@ -54,13 +67,20 @@ function buildCsp(): string { // Dev additionally needs 'unsafe-eval' (Turbopack HMR) plus a dev websocket // (added to connect-src below). - const scriptSrc = isDev - ? `script-src 'self' 'unsafe-inline' 'unsafe-eval' https://challenges.cloudflare.com` - : `script-src 'self' 'unsafe-inline' https://challenges.cloudflare.com` + const umami = umamiOrigin() + const scriptSrc = [ + "script-src 'self' 'unsafe-inline'", + isDev ? "'unsafe-eval'" : "", + "https://challenges.cloudflare.com", + umami, // load the Umami analytics script + ] + .filter(Boolean) + .join(" ") const connectSrc = [ "connect-src 'self'", isDev ? "ws: wss:" : "", "https://api.stripe.com https://api.openai.com https://challenges.cloudflare.com", + umami, // Umami event beacons (POST /api/send) sentry ?? "", ] .filter(Boolean)