diff --git a/next.config.ts b/next.config.ts index 33bf79d..6297ea2 100644 --- a/next.config.ts +++ b/next.config.ts @@ -55,6 +55,7 @@ const nextConfig: NextConfig = { value: 'geolocation=(), microphone=(), camera=(), payment=(), usb=()', }, // Content-Security-Policy is handled by @next-safe/middleware in src/middleware.ts + // Other security headers (HSTS, Referrer-Policy, Permissions-Policy) are static and configured here ], }, { diff --git a/src/middleware.ts b/src/middleware.ts index 5a93abd..a7e99b1 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,6 +1,6 @@ +import { chain, chainMatch, isPageRequest, csp } from '@next-safe/middleware'; import createMiddleware from 'next-intl/middleware'; import { locales, defaultLocale } from './i18n/config'; -import { NextRequest } from 'next/server'; const handleI18nRouting = createMiddleware({ locales, @@ -8,33 +8,27 @@ const handleI18nRouting = createMiddleware({ localePrefix: 'always', }); -export default function middleware(request: NextRequest) { - // Handle i18n routing first - const response = handleI18nRouting(request); +// Define CSP using @next-safe/middleware +const securityMiddleware = csp({ + directives: { + 'default-src': ["'self'"], + 'script-src': ["'self'", "'unsafe-inline'", "'unsafe-eval'"], + 'style-src': ["'self'", "'unsafe-inline'", 'https://fonts.googleapis.com'], + 'font-src': ["'self'", 'https://fonts.gstatic.com', 'data:'], + 'img-src': ["'self'", 'data:', 'blob:', 'https://mxadgucxhmstlzsbgmoz.supabase.co'], + 'connect-src': ["'self'", 'https://mxadgucxhmstlzsbgmoz.supabase.co'], + 'frame-ancestors': ["'self'"], + 'base-uri': ["'self'"], + 'form-action': ["'self'"], + }, +}); - // Add security headers to the response - response.headers.set( - 'Content-Security-Policy', - "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com data:; img-src 'self' data: blob: https://mxadgucxhmstlzsbgmoz.supabase.co; connect-src 'self' https://mxadgucxhmstlzsbgmoz.supabase.co; frame-ancestors 'self'; base-uri 'self'; form-action 'self'" - ); - - response.headers.set( - 'Strict-Transport-Security', - 'max-age=31536000; includeSubDomains; preload' - ); - - response.headers.set( - 'Referrer-Policy', - 'strict-origin-when-cross-origin' - ); - - response.headers.set( - 'Permissions-Policy', - 'geolocation=(), microphone=(), camera=(), payment=(), usb=()' - ); - - return response; -} +// Use chain to combine i18n middleware with security middleware +// First run i18n, then apply CSP only on page requests +export default chain( + handleI18nRouting, + chainMatch(isPageRequest)(securityMiddleware) +); export const config = { matcher: [