docs: Add docs on CSP and nonce generation (#54601)

There's been some confusion on the correct way to add a `nonce`, so took the opportunity here to:

- Add a new docs page for Content Security Policy
- Explained how to generate a `nonce` with Middleware
- Showed how to consume the `nonce` in a route with `headers`
- Updated the `with-strict-csp` example
- Update the `nonce` error message page
- Backlinked to the new page in a few places in the docs
This commit is contained in:
Lee Robinson 2023-09-01 17:13:49 -05:00 committed by GitHub
parent 03c3c004a1
commit 5eea161d8b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 327 additions and 308 deletions

View file

@ -0,0 +1,197 @@
---
title: Content Security Policy
description: Learn how to set a Content Security Policy (CSP) for your Next.js application.
related:
links:
- app/building-your-application/routing/middleware
- app/api-reference/functions/headers
---
{/* The content of this doc is shared between the app and pages router. You can use the `<PagesOnly>Content</PagesOnly>` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */}
[Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) is important to guard your Next.js application against various security threats such as cross-site scripting (XSS), clickjacking, and other code injection attacks.
By using CSP, developers can specify which origins are permissible for content sources, scripts, stylesheets, images, fonts, objects, media (audio, video), iframes, and more.
<details>
<summary>Examples</summary>
- [Strict CSP](https://github.com/vercel/next.js/tree/canary/examples/with-strict-csp)
</details>
## Nonces
A [nonce](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) is a unique, random string of characters created for a one-time use. It is used in conjunction with CSP to selectively allow certain inline scripts or styles to execute, bypassing strict CSP directives.
### Why use a nonce?
Even though CSPs are designed to block malicious scripts, there are legitimate scenarios where inline scripts are necessary. In such cases, nonces offer a way to allow these scripts to execute if they have the correct nonce.
### Adding a nonce with Middleware
[Middleware](/docs/app/building-your-application/routing/middleware) enables you to add headers and generate nonces before the page renders.
Every time a page is viewed, a fresh nonce should be generated. This means that you **must use dynamic rendering to add nonces**.
For example:
```ts filename="middleware.ts" switcher
import { NextRequest, NextResponse } from 'next/server'
export function middleware(request: NextRequest) {
const nonce = crypto.randomUUID()
const cspHeader = `
default-src 'self';
script-src 'self' 'nonce-${nonce}' 'strict-dynamic';
style-src 'self' 'nonce-${nonce}';
img-src 'self' blob: data:;
font-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
block-all-mixed-content;
upgrade-insecure-requests;
`
const requestHeaders = new Headers()
requestHeaders.set('x-nonce', nonce)
requestHeaders.set(
'Content-Security-Policy',
// Replace newline characters and spaces
cspHeader.replace(/\s{2,}/g, ' ').trim()
)
return NextResponse.next({
headers: requestHeaders,
request: {
headers: requestHeaders,
},
})
}
```
```js filename="middleware.js" switcher
import { NextResponse } from 'next/server'
export function middleware(request) {
const nonce = crypto.randomUUID()
const cspHeader = `
default-src 'self';
script-src 'self' 'nonce-${nonce}' 'strict-dynamic';
style-src 'self' 'nonce-${nonce}';
img-src 'self' blob: data:;
font-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
block-all-mixed-content;
upgrade-insecure-requests;
`
const requestHeaders = new Headers()
requestHeaders.set('x-nonce', nonce)
requestHeaders.set(
'Content-Security-Policy',
// Replace newline characters and spaces
cspHeader.replace(/\s{2,}/g, ' ').trim()
)
return NextResponse.next({
headers: requestHeaders,
request: {
headers: requestHeaders,
},
})
}
```
By default, Middleware runs on all requests. You can filter Middleware to run on specific paths using a [`matcher`](/docs/app/building-your-application/routing/middleware#matcher).
We recommend ignoring matching prefetches (from `next/link`) and static assets that don't need the CSP header.
```ts filename="middleware.ts" switcher
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
{
source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
missing: [
{ type: 'header', key: 'next-router-prefetch' },
{ type: 'header', key: 'purpose', value: 'prefetch' },
],
},
],
}
```
```js filename="middleware.js" switcher
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
{
source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
missing: [
{ type: 'header', key: 'next-router-prefetch' },
{ type: 'header', key: 'purpose', value: 'prefetch' },
],
},
],
}
```
### Reading the nonce
You can now read the nonce from a [Server Component](/docs/app/building-your-application/rendering/server-components) using [`headers`](/docs/app/api-reference/functions/headers):
```tsx filename="app/page.tsx" switcher
import { headers } from 'next/headers'
import Script from 'next/script'
export default function Page() {
const nonce = headers().get('x-nonce')
return (
<Script
src="https://www.googletagmanager.com/gtag/js"
strategy="afterInteractive"
nonce={nonce}
/>
)
}
```
```jsx filename="app/page.jsx" switcher
import { headers } from 'next/headers'
import Script from 'next/script'
export default function Page() {
const nonce = headers().get('x-nonce')
return (
<Script
src="https://www.googletagmanager.com/gtag/js"
strategy="afterInteractive"
nonce={nonce}
/>
)
}
```
## Version History
We recommend using `v13.4.20+` of Next.js to properly handle and apply nonces.

View file

@ -615,7 +615,7 @@ module.exports = {
### `dangerouslyAllowSVG`
The default [loader](#loader) does not optimize SVG images for a few reasons. First, SVG is a vector format meaning it can be resized losslessly. Second, SVG has many of the same features as HTML/CSS, which can lead to vulnerabilities without proper [Content Security Policy (CSP) headers](/docs/app/api-reference/next-config-js/headers).
The default [loader](#loader) does not optimize SVG images for a few reasons. First, SVG is a vector format meaning it can be resized losslessly. Second, SVG has many of the same features as HTML/CSS, which can lead to vulnerabilities without a proper [Content Security Policy](/docs/app/building-your-application/configuring/content-security-policy).
If you need to serve SVG images with the default Image Optimization API, you can set `dangerouslyAllowSVG` inside your `next.config.js`:

View file

@ -388,24 +388,50 @@ module.exports = {
## Cache-Control
You can set the `Cache-Control` header in your [Next.js API Routes](/docs/pages/building-your-application/routing/api-routes) by using the `res.setHeader` method:
You cannot set `Cache-Control` headers in `next.config.js` for pages or assets, as these headers will be overwritten in production to ensure that responses and static assets are cached effectively.
```js filename="pages/api/user.js"
export default function handler(req, res) {
<AppOnly>
Learn more about [caching](/docs/app/building-your-application/caching) with the App Router.
</AppOnly>
<PagesOnly>
If you need to revalidate the cache of a page that has been [statically generated](/docs/pages/building-your-application/rendering/static-site-generation), you can do so by setting the `revalidate` prop in the page's [`getStaticProps`](/docs/pages/building-your-application/data-fetching/get-static-props) function.
You can set the `Cache-Control` header in your [API Routes](/docs/pages/building-your-application/routing/api-routes) by using the `res.setHeader` method:
```ts filename="pages/api/hello.ts" switcher
import type { NextApiRequest, NextApiResponse } from 'next'
type ResponseData = {
message: string
}
export default function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseData>
) {
res.setHeader('Cache-Control', 's-maxage=86400')
res.status(200).json({ name: 'John Doe' })
res.status(200).json({ message: 'Hello from Next.js!' })
}
```
You cannot set `Cache-Control` headers in `next.config.js` file as these will be overwritten in production to ensure that API Routes and static assets are cached effectively.
```js filename="pages/api/hello.js" switcher
export default function handler(req, res) {
res.setHeader('Cache-Control', 's-maxage=86400')
res.status(200).json({ message: 'Hello from Next.js!' })
}
```
If you need to revalidate the cache of a page that has been [statically generated](/docs/pages/building-your-application/rendering/static-site-generation), you can do so by setting the `revalidate` prop in the page's [`getStaticProps`](/docs/pages/building-your-application/data-fetching/get-static-props) function.
</PagesOnly>
## Options
### X-DNS-Prefetch-Control
This header controls DNS prefetching, allowing browsers to proactively perform domain name resolution on external links, images, CSS, JavaScript, and more. This prefetching is performed in the background, so the [DNS](https://developer.mozilla.org/en-US/docs/Glossary/DNS) is more likely to be resolved by the time the referenced items are needed. This reduces latency when the user clicks a link.
[This header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-DNS-Prefetch-Control) controls DNS prefetching, allowing browsers to proactively perform domain name resolution on external links, images, CSS, JavaScript, and more. This prefetching is performed in the background, so the [DNS](https://developer.mozilla.org/en-US/docs/Glossary/DNS) is more likely to be resolved by the time the referenced items are needed. This reduces latency when the user clicks a link.
```js
{
@ -416,19 +442,9 @@ This header controls DNS prefetching, allowing browsers to proactively perform d
### Strict-Transport-Security
This header informs browsers it should only be accessed using HTTPS, instead of using HTTP. Using the configuration below, all present and future subdomains will use HTTPS for a `max-age` of 2 years. This blocks access to pages or subdomains that can only be served over HTTP.
[This header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) informs browsers it should only be accessed using HTTPS, instead of using HTTP. Using the configuration below, all present and future subdomains will use HTTPS for a `max-age` of 2 years. This blocks access to pages or subdomains that can only be served over HTTP.
<AppOnly>
If you're deploying to [Vercel](https://vercel.com/docs/concepts/edge-network/headers#strict-transport-security?utm_source=next-site&utm_medium=docs&utm_campaign=next-website), this header is not necessary as it's automatically added to all deployments unless you declare [`headers`](/docs/app/api-reference/next-config-js/headers) in your `next.config.js`.
</AppOnly>
<PagesOnly>
If you're deploying to [Vercel](https://vercel.com/docs/concepts/edge-network/headers#strict-transport-security?utm_source=next-site&utm_medium=docs&utm_campaign=next-website), this header is not necessary as it's automatically added to all deployments unless you declare [`headers`](/docs/pages/api-reference/next-config-js/headers) in your `next.config.js`.
</PagesOnly>
If you're deploying to [Vercel](https://vercel.com/docs/concepts/edge-network/headers#strict-transport-security?utm_source=next-site&utm_medium=docs&utm_campaign=next-website), this header is not necessary as it's automatically added to all deployments unless you declare `headers` in your `next.config.js`.
```js
{
@ -439,7 +455,9 @@ If you're deploying to [Vercel](https://vercel.com/docs/concepts/edge-network/he
### X-Frame-Options
This header indicates whether the site should be allowed to be displayed within an `iframe`. This can prevent against clickjacking attacks. This header has been superseded by CSP's `frame-ancestors` option, which has better support in modern browsers.
[This header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) indicates whether the site should be allowed to be displayed within an `iframe`. This can prevent against clickjacking attacks.
**This header has been superseded by CSP's `frame-ancestors` option**, which has better support in modern browsers.
```js
{
@ -450,7 +468,7 @@ This header indicates whether the site should be allowed to be displayed within
### Permissions-Policy
This header allows you to control which features and APIs can be used in the browser. It was previously named `Feature-Policy`. You can view the full list of permission options [here](https://github.com/w3c/webappsec-permissions-policy/blob/main/features.md).
[This header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy) allows you to control which features and APIs can be used in the browser. It was previously named `Feature-Policy`.
```js
{
@ -461,7 +479,9 @@ This header allows you to control which features and APIs can be used in the bro
### X-Content-Type-Options
This header prevents the browser from attempting to guess the type of content if the `Content-Type` header is not explicitly set. This can prevent XSS exploits for websites that allow users to upload and share files. For example, a user trying to download an image, but having it treated as a different `Content-Type` like an executable, which could be malicious. This header also applies to downloading browser extensions. The only valid value for this header is `nosniff`.
[This header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options) prevents the browser from attempting to guess the type of content if the `Content-Type` header is not explicitly set. This can prevent XSS exploits for websites that allow users to upload and share files.
For example, a user trying to download an image, but having it treated as a different `Content-Type` like an executable, which could be malicious. This header also applies to downloading browser extensions. The only valid value for this header is `nosniff`.
```js
{
@ -472,7 +492,7 @@ This header prevents the browser from attempting to guess the type of content if
### Referrer-Policy
This header controls how much information the browser includes when navigating from the current website (origin) to another. You can read about the different options [here](https://scotthelme.co.uk/a-new-security-header-referrer-policy/).
[This header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy) controls how much information the browser includes when navigating from the current website (origin) to another.
```js
{
@ -483,35 +503,7 @@ This header controls how much information the browser includes when navigating f
### Content-Security-Policy
This header helps prevent cross-site scripting (XSS), clickjacking and other code injection attacks. Content Security Policy (CSP) can specify allowed origins for content including scripts, stylesheets, images, fonts, objects, media (audio, video), iframes, and more.
You can read about the many different CSP options [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP).
You can add Content Security Policy directives using a template string.
```js
// Before defining your Security Headers
// add Content Security Policy directives using a template string.
const ContentSecurityPolicy = `
default-src 'self';
script-src 'self';
child-src example.com;
style-src 'self' example.com;
font-src 'self';
`
```
When a directive uses a keyword such as `self`, wrap it in single quotes `''`.
In the header's value, replace the new line with a space.
```js
{
key: 'Content-Security-Policy',
value: ContentSecurityPolicy.replace(/\s{2,}/g, ' ').trim()
}
```
Learn more about adding a [Content Security Policy](/docs/app/building-your-application/configuring/content-security-policy) to your application.
## Version History

View file

@ -0,0 +1,7 @@
---
title: Content Security Policy
description: Learn how to set a Content Security Policy (CSP) for your Next.js application.
source: app/building-your-application/configuring/content-security-policy
---
{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `<PagesOnly>Content</PagesOnly>` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */}

View file

@ -22,6 +22,7 @@ Before taking your Next.js application to production, here are some recommendati
- [Automatic Font Optimization](/docs/pages/building-your-application/optimizing/fonts)
- [Script Optimization](/docs/pages/building-your-application/optimizing/scripts)
- Improve [loading performance](#loading-performance)
- Consider adding a [Content Security Policy](/docs/pages/building-your-application/configuring/content-security-policy)
## Caching

View file

@ -4,8 +4,8 @@ title: nonce contained invalid characters
## Why This Error Occurred
This happens when there is a request that contains a `Content-Security-Policy`
header that contains a `script-src` directive with a nonce value that contains
A request to your Next.js application contained a `Content-Security-Policy`
header with a `script-src` directive and `nonce` that contains
invalid characters (any one of `<>&` characters). For example:
- `'nonce-<script />'`: not allowed
@ -15,8 +15,10 @@ invalid characters (any one of `<>&` characters). For example:
## Possible Ways to Fix It
Replace the nonce value with a base64 encoded value.
We recommend using a randomly generated UUID for your nonce.
Learn more about how to use nonces with Next.js in our [Content Security Policy](/docs/app/building-your-application/configuring/content-security-policy) docs.
## Useful Links
- [Content Security Policy Sources](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/Sources#sources)
- [Content Security Policy](/docs/app/building-your-application/configuring/content-security-policy)

View file

@ -1,35 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

View file

@ -1,33 +0,0 @@
# Styled-JSX with Content Security Policy
This example showcases how you can use `nonce` for `style-src` directive in `Content Security Policy` with `styled-jsx`.
Checkout the [demo](https://styled-jsx-with-csp.vercel.app/) and notice the following,
- `style-src` directive in `Content-Security-Policy` response header.
- `meta` tag to pass on the `nonce` to styled-jsx for client-side rendering.
- `style` tags with `nonce` attributes.
## Deploy your own
Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example) or preview live with [StackBlitz](https://stackblitz.com/github/vercel/next.js/tree/canary/examples/styled-jsx-with-csp)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vercel/next.js/tree/canary/examples/styled-jsx-with-csp&project-name=styled-jsx-with-csp&repository-name=styled-jsx-with-csp)
## How to use
Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example:
```bash
npx create-next-app --example styled-jsx-with-csp styled-jsx-with-csp-app
```
```bash
yarn create next-app --example styled-jsx-with-csp styled-jsx-with-csp-app
```
```bash
pnpm create next-app --example styled-jsx-with-csp styled-jsx-with-csp-app
```
Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).

View file

@ -1,5 +0,0 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

View file

@ -1,20 +0,0 @@
{
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
},
"dependencies": {
"nanoid": "^4.0.0",
"next": "latest",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/node": "^18.11.5",
"@types/react": "^18.0.23",
"@types/react-dom": "^18.0.7",
"typescript": "^4.8.4"
}
}

View file

@ -1,8 +0,0 @@
import type { AppProps } from 'next/app'
export default function CustomApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />
}
// Disable static optimization to always server render, making nonce unique on every request
CustomApp.getInitialProps = () => ({})

View file

@ -1,50 +0,0 @@
import Document, {
Html,
Head,
Main,
NextScript,
DocumentContext,
} from 'next/document'
import { nanoid } from 'nanoid'
class CustomDocument extends Document {
public props: any
static async getInitialProps(ctx: DocumentContext) {
const nonce = nanoid()
const docProps = await ctx.defaultGetInitialProps(ctx, { nonce })
let contentSecurityPolicy = ''
if (process.env.NODE_ENV === 'production') {
contentSecurityPolicy = `default-src 'self'; style-src 'nonce-${nonce}';`
} else {
// react-refresh needs 'unsafe-eval'
// Next.js needs 'unsafe-inline' during development https://github.com/vercel/next.js/blob/canary/packages/next/client/dev/fouc.js
// Specifying 'nonce' makes a modern browsers ignore 'unsafe-inline'
contentSecurityPolicy = `default-src 'self'; style-src 'unsafe-inline'; script-src 'self' 'unsafe-eval';`
}
ctx.res?.setHeader('Content-Security-Policy', contentSecurityPolicy)
return { ...docProps, nonce }
}
render() {
return (
<Html>
<Head>
{/* Styled-JSX will add this `nonce` to style tags on Client Side Rendering */}
{/* https://github.com/vercel/styled-jsx/blob/master/src/lib/stylesheet.js#L31 */}
{/* https://github.com/vercel/styled-jsx/blob/master/src/lib/stylesheet.js#L240 */}
<meta property="csp-nonce" content={this.props.nonce} />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default CustomDocument

View file

@ -1,40 +0,0 @@
import { useState } from 'react'
function ClientSideComponent() {
return (
<>
<style jsx>
{`
.title {
font-size: 24px;
color: green;
}
`}
</style>
<p className="title">This is rendered on client-side</p>
</>
)
}
export default function Home() {
const [isVisible, setVisibility] = useState(false)
const toggleVisibility = () => {
setVisibility((prevState) => !prevState)
}
return (
<>
<style jsx>
{`
.title {
font-size: 24px;
}
`}
</style>
<p className="title">Styled-JSX with Content Security Policy</p>
<button onClick={toggleVisibility}>Toggle</button>
{isVisible ? <ClientSideComponent /> : null}
</>
)
}

View file

@ -1,20 +0,0 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}

View file

@ -1,14 +1,9 @@
# Example app with strict CSP generating script hash
# Content Security Policy with Nonce
This example features how you can set up a strict CSP for your pages including Next.js' inline bootstrap script by hash.
It defines the CSP by document `meta` tag.
Note: There are still valid cases for using a nonce in case you need to inline scripts or styles for which calculating a hash is not feasible.
This example shows how to create a Next.js application that sets a strict Content Security Policy (CSP) for your pages, including generating a dynamic nonce.
## Deploy your own
Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example) or preview live with [StackBlitz](https://stackblitz.com/github/vercel/next.js/tree/canary/examples/with-strict-csp)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vercel/next.js/tree/canary/examples/with-strict-csp&project-name=with-strict-csp&repository-name=with-strict-csp)
## How to use

View file

@ -0,0 +1,7 @@
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}

View file

@ -0,0 +1,8 @@
import { headers } from 'next/headers'
import Script from 'next/script'
export default function Page() {
const nonce = headers().get('x-nonce')
return <Script src="https://..." strategy="afterInteractive" nonce={nonce} />
}

View file

@ -0,0 +1,54 @@
import { NextResponse } from 'next/server'
export function middleware(request) {
const nonce = crypto.randomUUID()
const cspHeader = `
default-src 'self';
script-src 'self' 'nonce-${nonce}' 'strict-dynamic';
style-src 'self' 'nonce-${nonce}';
img-src 'self' blob: data:;
font-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
block-all-mixed-content;
upgrade-insecure-requests;
`
const requestHeaders = new Headers()
// Setting request headers
requestHeaders.set('x-nonce', nonce)
requestHeaders.set(
'Content-Security-Policy',
// Replace newline characters and spaces
cspHeader.replace(/\s{2,}/g, ' ').trim()
)
return NextResponse.next({
headers: requestHeaders,
request: {
headers: requestHeaders,
},
})
}
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
{
source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
missing: [
{ type: 'header', key: 'next-router-prefetch' },
{ type: 'header', key: 'purpose', value: 'prefetch' },
],
},
],
}

View file

@ -1,30 +0,0 @@
import crypto from 'crypto'
import { Html, Head, Main, NextScript } from 'next/document'
const cspHashOf = (text) => {
const hash = crypto.createHash('sha256')
hash.update(text)
return `'sha256-${hash.digest('base64')}'`
}
export default function Document(ctx) {
let csp = `default-src 'self'; script-src 'self' ${cspHashOf(
NextScript.getInlineScriptSource(ctx)
)}`
if (process.env.NODE_ENV !== 'production') {
csp = `style-src 'self' 'unsafe-inline'; font-src 'self' data:; default-src 'self'; script-src 'unsafe-eval' 'self' ${cspHashOf(
NextScript.getInlineScriptSource(ctx)
)}`
}
return (
<Html>
<Head>
<meta httpEquiv="Content-Security-Policy" content={csp} />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}

View file

@ -1,3 +0,0 @@
export default function Home() {
return <div>Hello World</div>
}