A simple example showing how to get the default Next.js template working with a secure Content Security Policy (CSP).
The project was created using: $ npx create-next-app@latest
in /middleware.js
import { NextResponse } from 'next/server'
export function middleware(request) {
const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
const cspHeader = `
default-src 'self';
script-src 'self' 'nonce-${nonce}' ${
process.env.NODE_ENV === "production" ? `` : `'unsafe-eval'`
};
style-src 'self' 'unsafe-inline';
img-src 'self' blob: data:;
font-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
upgrade-insecure-requests;
`;
// Replace newline characters and spaces
const contentSecurityPolicyHeaderValue = cspHeader
.replace(/\s{2,}/g, ' ')
.trim()
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-nonce', nonce)
requestHeaders.set(
'Content-Security-Policy',
contentSecurityPolicyHeaderValue
)
const response = NextResponse.next({
request: {
headers: requestHeaders,
},
})
response.headers.set(
'Content-Security-Policy',
contentSecurityPolicyHeaderValue
)
return response
}
in /app/layout.js
export const dynamic = "force-dynamic";
Override the default Next/Image component and remove the inline style (style="color:transparent" )
in /app/_components/image.js
import { getImageProps } from "next/image"
export default function Image(props) {
const { props: nextProps } = getImageProps({
...props
})
const { style: _omit, ...delegated } = nextProps
return <img {...delegated} />
}
Finally, update the imports in /app/page.js
// import Image from "next/image";
import Image from "./_components/image";
The CSP should now work in development and production modes without error.
$ npm run dev
$ npm run build && npm run start
Remove Next/image inline style
Issues relating to next/image, inline syles and CSP - #61388, #45184.