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