This is a simple explanation of how to set up a secure content security policy (CSP) with a default create-next-app project. A working example can be found on github.
To begin with, create a new Next.js project with the default template and the default prompt settings:
Generate nonces with middleware
A CSP can be set using custom headers in next.config.js, but Next.js includes inline scripts and styles that are will be blocked by a secure CSP using:
We can add unsafe-inline
to the script-src and style-src directives to stop the scripts being blocked, but obviously this is not a secure solution.
To securely allow the inline scripts and styles, a hash-based or nonce-based policy can be used. A nonce-based policy can be implemented in Next.js with middleware. The Next.js docs include an explanation and example implementation of this.
We can add nonces to both the script-src and style-src diretives, so we can securely allow the inline scripts and styles that Next.js includes.
When the middleware is set up, nonces are automatically to the scripts and styles than Next.js injects.
Force dynamic rendering
When using nonces, a fresh nonce must be generated on each new page-view. To do this you need to 'force dynamic rendering' in Next.js by adding the follwing to the root layout.js page.
Allow 'unsafe-eval' in dev mode
The Next.js development server uses the Javasript eval
funtion, so we need to allow 'unsafe-eval'
in development mode.
Inline style images
Next/Image adds an inline style (style="color:transparent"
) to the Img tag that the Next/Image component exports. The default template includes two next/image tags and thus inline styles that will be blocked buy the secure CSP. A nonce can not be added to inline styles, so an alternative approach is needed in this case.
There are open issues relating to this problem - #61388, #45184.
Once solution is to override the next/image component and remove the inline style. The following example is adapted from Sneko's solution using this approach:
Then you just need to replace the next/image component import in Page.js with the local version:
Summary
To get the default Next.js template working with a secure CSP, you need to do the following:
1. Add a nonce-based CSP using middleware
in /middleware.js
2. Force dynamic rendering
in /app/layout.js
3. Remove next/image inline styles
Override the default next/image component and remove the inline style.
in /app/_components/image.js
Finally, update the imports in /app/page.js
The CSP should now work in development and production modes without error.
$ npm run dev
$ npm run build && npm run start