import type { NextConfig } from "next"; const storagePublicUrl = process.env["STORAGE_PUBLIC_URL"] || "http://localhost:9000"; const storageUrl = new URL(storagePublicUrl); const storageOrigin = storageUrl.origin; // unsafe-eval is only needed by Next.js dev's HMR/Fast Refresh — production // builds never eval(), so drop it there instead of leaving XSS payloads a // wide-open eval sink in the deployed app. const scriptSrc = process.env.NODE_ENV === "production" ? "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net" : "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net"; const securityHeaders = [ { key: "X-Content-Type-Options", value: "nosniff", }, { key: "X-Frame-Options", value: "DENY", }, { key: "Referrer-Policy", value: "strict-origin-when-cross-origin", }, { key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload", }, { key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=(), interest-cohort=()", }, { key: "X-DNS-Prefetch-Control", value: "on", }, { key: "Content-Security-Policy", value: [ "default-src 'self'", scriptSrc, // jsdelivr serves the Scalar API-reference bundle on /docs. "style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net", `img-src 'self' data: blob: ${storageOrigin} https://www.gravatar.com`, "font-src 'self' https://cdn.jsdelivr.net data:", `connect-src 'self' ${storageOrigin} https://cdn.jsdelivr.net`, "frame-ancestors 'none'", "base-uri 'self'", "form-action 'self'", ].join("; "), }, ]; const nextConfig: NextConfig = { output: "standalone", images: { remotePatterns: [ { protocol: storageUrl.protocol.replace(":", "") as "http" | "https", hostname: storageUrl.hostname, port: storageUrl.port, pathname: "/**", }, ], }, async headers() { return [ { source: "/(.*)", headers: securityHeaders, }, ]; }, }; export default nextConfig;