Skip to main content

How to implement security headers in a Next.js application?

More information regarding HTTP security headers

HTTP headers can be specified in the next.config.js file.

Example using the above recommended values:

...
async headers() {
const csp = generateCsp();
return [
{
// Apply these headers to all routes in your application.
source: '/:path*',
headers: [
{
key: 'X-DNS-Prefetch-Control',
value: 'on'
},
{
key: 'Strict-Transport-Security',
value: 'max-age=63072000; includeSubDomains; preload'
},
{
key: 'X-XSS-Protection',
value: '1; mode=block'
},
{
key: 'X-Frame-Options',
value: 'DENY'
},
{
key: 'X-Content-Type-Options',
value: 'nosniff'
},
{
key: 'Referrer-Policy',
value: 'strict-origin-when-cross-origin'
},
{
key: 'Permissions-Policy',
value: 'accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=()'
},
],
},
]
}
...

More info - Next.js Security Headers

Cache-control

This header is handled by Next.js, so if it's specified in the next.config.js file, it will be overridden.

Content-Security-Policy

Basic implementation

In a basic Next.js application, that doesn't have several content sources or UI libraries such as MaterialUI or Styled components, this header can be implemented as Next.js documentation suggest.

Otherwise, you may need to execute inline scripts and a stricter policy. So implementing this header might be a little tricky.

Using meta tag and hashes

To implement a strict CSP using the HTML meta tag, you can check out this repo. This example implements a CSP using hashes of the injected inline scripts.

In some cases calculating a hash is not possible, so you need to use a nonce.

HTTP header + SSR + Nonce

A nonce is a randomly generated string that is only used once, therefore you need to add server middleware to generate one on each request.

For example:

export const middleware = (req) => {
const res = NextResponse.next();
res.headers.append('Content-Security-Policy', generateCspWithNonce());
return res;
}

Example using custom server and helmet library

HTTP header + ISG + Nonce

Although is not the ideal implementation, you can generate a static nonce for a static page (is more secure than using 'unsafe-eval' but less secure than using a dynamic nonce).

In this case, you can generate a nonce at build time and pass it in the next.config.js or in a middleware if you use revalidation.

Styled components + Nonce

If your app use styled components, you should add this in your code:

<script
type='text/javascript'
dangerouslySetInnerHTML={{ __html: `window.__webpack_nonce__="${nonce}"` }}
nonce={nonce}
/>

References

Configuring CSP with MaterialUI

Next.js feature request: Nonce or hash for inline styles

CSP with HTTP header for inline scripts

Can you get pwned with CSS?