import type { NextConfig } from "next"; const nextConfig: NextConfig = { // Image optimization – whitelist only known-safe domains (no wildcard **) images: { remotePatterns: [ // GitHub avatars (OAuth) { protocol: "https", hostname: "avatars.githubusercontent.com" }, // Google avatars (OAuth) { protocol: "https", hostname: "lh3.googleusercontent.com" }, { protocol: "https", hostname: "lh4.googleusercontent.com" }, { protocol: "https", hostname: "lh5.googleusercontent.com" }, // Stack Auth CDN { protocol: "https", hostname: "*.stackauth.com" }, // Prompt preview / gallery images (Cloudflare R2 / storage) { protocol: "https", hostname: "imagedelivery.net" }, { protocol: "https", hostname: "images.unsplash.com" }, // AI image CDNs { protocol: "https", hostname: "oaidalleapiprodscus.blob.core.windows.net" }, { protocol: "https", hostname: "cdn.midjourney.com" }, { protocol: "https", hostname: "image.civitai.com" }, ], formats: ["image/avif", "image/webp"], }, // Security headers async headers() { return [ { source: "/(.*)", headers: [ { key: "X-Frame-Options", value: "SAMEORIGIN" }, { key: "X-Content-Type-Options", value: "nosniff" }, { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" }, { key: "X-DNS-Prefetch-Control", value: "on" }, { key: "X-Permitted-Cross-Domain-Policies", value: "none" }, { key: "Strict-Transport-Security", value: "max-age=31536000; includeSubDomains; preload" }, { key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=()" }, { key: "Content-Security-Policy", value: [ "default-src 'self'", "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://challenges.cloudflare.com https://www.googletagmanager.com", "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com", "font-src 'self' https://fonts.gstatic.com", "img-src 'self' data: blob: https:", "frame-src 'self' https://challenges.cloudflare.com", "connect-src 'self' https://api.openai.com https://api.anthropic.com https://generativelanguage.googleapis.com https://challenges.cloudflare.com wss: ws:", "media-src 'self' blob:", "worker-src 'self' blob:", ].join("; "), }, ], }, { source: "/logos/(.*)", headers: [ { key: "Cache-Control", value: "public, max-age=31536000, immutable" }, ], }, { source: "/(.*\\.(?:svg|png|jpg|jpeg|gif|ico|woff2|woff|ttf))", headers: [ { key: "Cache-Control", value: "public, max-age=86400, stale-while-revalidate=3600" }, ], }, ]; }, // Performance optimizations – tree-shake large packages experimental: { optimizePackageImports: [ "lucide-react", "react-icons", "framer-motion", "recharts", "three", "@react-three/fiber", "@react-three/drei", "@radix-ui/react-dropdown-menu", "@radix-ui/react-select", "@radix-ui/react-tabs", "@radix-ui/react-tooltip", "@radix-ui/react-checkbox", "react-markdown", "zustand", ], }, }; export default nextConfig;