diff --git a/backend/src/config/configuration.ts b/backend/src/config/configuration.ts index 365691c..32b70d6 100644 --- a/backend/src/config/configuration.ts +++ b/backend/src/config/configuration.ts @@ -1,5 +1,14 @@ +// FRONTEND_URL may hold a comma-separated list of origins (used for CORS). +// Domain/host resolution must only ever look at a single URL, otherwise +// `new URL('https://a.com,https://b.com')` yields the garbage hostname +// "a.com,https" which then leaks into ingress hosts and gets rejected by k8s. +function firstFrontendUrl(): string | undefined { + const first = (process.env.FRONTEND_URL || '').split(',')[0]?.trim(); + return first || undefined; +} + function resolvePlatformDomainFromEnv(): string { - const frontendUrl = process.env.FRONTEND_URL; + const frontendUrl = firstFrontendUrl(); if (frontendUrl) { try { const url = new URL(frontendUrl); @@ -35,7 +44,7 @@ function resolvePreviewRootDomainFromEnv(): string { return explicit.trim().toLowerCase(); } - const frontendUrl = process.env.FRONTEND_URL; + const frontendUrl = firstFrontendUrl(); if (frontendUrl) { try { const url = new URL(frontendUrl); diff --git a/backend/src/main.ts b/backend/src/main.ts index 6984a32..19dcf58 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -25,8 +25,13 @@ async function bootstrap() { // Security app.use(helmet()); + // FRONTEND_URL may be a comma-separated list of allowed origins. + const corsOrigins = (process.env.FRONTEND_URL || 'http://localhost:3000') + .split(',') + .map((o) => o.trim()) + .filter(Boolean); app.enableCors({ - origin: process.env.FRONTEND_URL || 'http://localhost:3000', + origin: corsOrigins, credentials: true, });