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 <noreply@anthropic.com>
This commit is contained in:
2026-01-25 12:31:19 +01:00
co-authored by Claude Sonnet 4.5
parent 011ed72cfa
commit cc1217e929
2 changed files with 31 additions and 17 deletions
+1 -16
View File
@@ -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)
],
},
{
+30 -1
View File
@@ -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: [
'/',