From cc1217e929947ea5de6a654db9ce20f77998c72d Mon Sep 17 00:00:00 2001 From: Damjan Savic Date: Sun, 25 Jan 2026 12:31:19 +0100 Subject: [PATCH] fix: move security headers to middleware composition (qa-requested) - Move CSP, HSTS, Referrer-Policy, and Permissions-Policy from next.config.ts to middleware - Compose headers with next-intl middleware to ensure they propagate through rewrites - Security headers now set via response.headers.set() in middleware after i18n routing - All security headers should now appear in HTTP responses Fixes QA issue: headers from next.config.ts not appearing due to middleware rewrites Co-Authored-By: Claude Sonnet 4.5 --- next.config.ts | 17 +---------------- src/middleware.ts | 31 ++++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/next.config.ts b/next.config.ts index 6994baa..101d19d 100644 --- a/next.config.ts +++ b/next.config.ts @@ -42,22 +42,7 @@ const nextConfig: NextConfig = { key: 'X-Content-Type-Options', value: 'nosniff', }, - { - key: 'Content-Security-Policy', - value: "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'", - }, - { - key: 'Strict-Transport-Security', - value: 'max-age=31536000; includeSubDomains; preload', - }, - { - key: 'Referrer-Policy', - value: 'strict-origin-when-cross-origin', - }, - { - key: 'Permissions-Policy', - value: 'geolocation=(), microphone=(), camera=(), payment=(), usb=()', - }, + // Security headers moved to middleware (CSP, HSTS, Referrer-Policy, Permissions-Policy) ], }, { diff --git a/src/middleware.ts b/src/middleware.ts index d815ec3..5a93abd 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,12 +1,41 @@ import createMiddleware from 'next-intl/middleware'; import { locales, defaultLocale } from './i18n/config'; +import { NextRequest } from 'next/server'; -export default createMiddleware({ +const handleI18nRouting = createMiddleware({ locales, defaultLocale, localePrefix: 'always', }); +export default function middleware(request: NextRequest) { + // Handle i18n routing first + const response = handleI18nRouting(request); + + // 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; +} + export const config = { matcher: [ '/',