Skip to content

Commit 5eea161

Browse files
authored
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
1 parent 03c3c00 commit 5eea161

File tree

20 files changed

+327
-308
lines changed

20 files changed

+327
-308
lines changed
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
---
2+
title: Content Security Policy
3+
description: Learn how to set a Content Security Policy (CSP) for your Next.js application.
4+
related:
5+
links:
6+
- app/building-your-application/routing/middleware
7+
- app/api-reference/functions/headers
8+
---
9+
10+
{/* 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. */}
11+
12+
[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.
13+
14+
By using CSP, developers can specify which origins are permissible for content sources, scripts, stylesheets, images, fonts, objects, media (audio, video), iframes, and more.
15+
16+
<details>
17+
<summary>Examples</summary>
18+
19+
- [Strict CSP](https://github.com/vercel/next.js/tree/canary/examples/with-strict-csp)
20+
21+
</details>
22+
23+
## Nonces
24+
25+
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.
26+
27+
### Why use a nonce?
28+
29+
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.
30+
31+
### Adding a nonce with Middleware
32+
33+
[Middleware](/docs/app/building-your-application/routing/middleware) enables you to add headers and generate nonces before the page renders.
34+
35+
Every time a page is viewed, a fresh nonce should be generated. This means that you **must use dynamic rendering to add nonces**.
36+
37+
For example:
38+
39+
```ts filename="middleware.ts" switcher
40+
import { NextRequest, NextResponse } from 'next/server'
41+
42+
export function middleware(request: NextRequest) {
43+
const nonce = crypto.randomUUID()
44+
const cspHeader = `
45+
default-src 'self';
46+
script-src 'self' 'nonce-${nonce}' 'strict-dynamic';
47+
style-src 'self' 'nonce-${nonce}';
48+
img-src 'self' blob: data:;
49+
font-src 'self';
50+
object-src 'none';
51+
base-uri 'self';
52+
form-action 'self';
53+
frame-ancestors 'none';
54+
block-all-mixed-content;
55+
upgrade-insecure-requests;
56+
`
57+
58+
const requestHeaders = new Headers()
59+
requestHeaders.set('x-nonce', nonce)
60+
requestHeaders.set(
61+
'Content-Security-Policy',
62+
// Replace newline characters and spaces
63+
cspHeader.replace(/\s{2,}/g, ' ').trim()
64+
)
65+
66+
return NextResponse.next({
67+
headers: requestHeaders,
68+
request: {
69+
headers: requestHeaders,
70+
},
71+
})
72+
}
73+
```
74+
75+
```js filename="middleware.js" switcher
76+
import { NextResponse } from 'next/server'
77+
78+
export function middleware(request) {
79+
const nonce = crypto.randomUUID()
80+
const cspHeader = `
81+
default-src 'self';
82+
script-src 'self' 'nonce-${nonce}' 'strict-dynamic';
83+
style-src 'self' 'nonce-${nonce}';
84+
img-src 'self' blob: data:;
85+
font-src 'self';
86+
object-src 'none';
87+
base-uri 'self';
88+
form-action 'self';
89+
frame-ancestors 'none';
90+
block-all-mixed-content;
91+
upgrade-insecure-requests;
92+
`
93+
94+
const requestHeaders = new Headers()
95+
requestHeaders.set('x-nonce', nonce)
96+
requestHeaders.set(
97+
'Content-Security-Policy',
98+
// Replace newline characters and spaces
99+
cspHeader.replace(/\s{2,}/g, ' ').trim()
100+
)
101+
102+
return NextResponse.next({
103+
headers: requestHeaders,
104+
request: {
105+
headers: requestHeaders,
106+
},
107+
})
108+
}
109+
```
110+
111+
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).
112+
113+
We recommend ignoring matching prefetches (from `next/link`) and static assets that don't need the CSP header.
114+
115+
```ts filename="middleware.ts" switcher
116+
export const config = {
117+
matcher: [
118+
/*
119+
* Match all request paths except for the ones starting with:
120+
* - api (API routes)
121+
* - _next/static (static files)
122+
* - _next/image (image optimization files)
123+
* - favicon.ico (favicon file)
124+
*/
125+
{
126+
source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
127+
missing: [
128+
{ type: 'header', key: 'next-router-prefetch' },
129+
{ type: 'header', key: 'purpose', value: 'prefetch' },
130+
],
131+
},
132+
],
133+
}
134+
```
135+
136+
```js filename="middleware.js" switcher
137+
export const config = {
138+
matcher: [
139+
/*
140+
* Match all request paths except for the ones starting with:
141+
* - api (API routes)
142+
* - _next/static (static files)
143+
* - _next/image (image optimization files)
144+
* - favicon.ico (favicon file)
145+
*/
146+
{
147+
source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
148+
missing: [
149+
{ type: 'header', key: 'next-router-prefetch' },
150+
{ type: 'header', key: 'purpose', value: 'prefetch' },
151+
],
152+
},
153+
],
154+
}
155+
```
156+
157+
### Reading the nonce
158+
159+
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):
160+
161+
```tsx filename="app/page.tsx" switcher
162+
import { headers } from 'next/headers'
163+
import Script from 'next/script'
164+
165+
export default function Page() {
166+
const nonce = headers().get('x-nonce')
167+
168+
return (
169+
<Script
170+
src="https://www.googletagmanager.com/gtag/js"
171+
strategy="afterInteractive"
172+
nonce={nonce}
173+
/>
174+
)
175+
}
176+
```
177+
178+
```jsx filename="app/page.jsx" switcher
179+
import { headers } from 'next/headers'
180+
import Script from 'next/script'
181+
182+
export default function Page() {
183+
const nonce = headers().get('x-nonce')
184+
185+
return (
186+
<Script
187+
src="https://www.googletagmanager.com/gtag/js"
188+
strategy="afterInteractive"
189+
nonce={nonce}
190+
/>
191+
)
192+
}
193+
```
194+
195+
## Version History
196+
197+
We recommend using `v13.4.20+` of Next.js to properly handle and apply nonces.

docs/02-app/02-api-reference/01-components/image.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -615,7 +615,7 @@ module.exports = {
615615

616616
### `dangerouslyAllowSVG`
617617

618-
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).
618+
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).
619619

620620
If you need to serve SVG images with the default Image Optimization API, you can set `dangerouslyAllowSVG` inside your `next.config.js`:
621621

docs/02-app/02-api-reference/05-next-config-js/headers.mdx

Lines changed: 44 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -388,24 +388,50 @@ module.exports = {
388388

389389
## Cache-Control
390390

391-
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:
391+
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.
392392

393-
```js filename="pages/api/user.js"
394-
export default function handler(req, res) {
393+
<AppOnly>
394+
395+
Learn more about [caching](/docs/app/building-your-application/caching) with the App Router.
396+
397+
</AppOnly>
398+
399+
<PagesOnly>
400+
401+
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.
402+
403+
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:
404+
405+
```ts filename="pages/api/hello.ts" switcher
406+
import type { NextApiRequest, NextApiResponse } from 'next'
407+
408+
type ResponseData = {
409+
message: string
410+
}
411+
412+
export default function handler(
413+
req: NextApiRequest,
414+
res: NextApiResponse<ResponseData>
415+
) {
395416
res.setHeader('Cache-Control', 's-maxage=86400')
396-
res.status(200).json({ name: 'John Doe' })
417+
res.status(200).json({ message: 'Hello from Next.js!' })
397418
}
398419
```
399420

400-
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.
421+
```js filename="pages/api/hello.js" switcher
422+
export default function handler(req, res) {
423+
res.setHeader('Cache-Control', 's-maxage=86400')
424+
res.status(200).json({ message: 'Hello from Next.js!' })
425+
}
426+
```
401427

402-
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.
428+
</PagesOnly>
403429

404430
## Options
405431

406432
### X-DNS-Prefetch-Control
407433

408-
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.
434+
[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.
409435

410436
```js
411437
{
@@ -416,19 +442,9 @@ This header controls DNS prefetching, allowing browsers to proactively perform d
416442

417443
### Strict-Transport-Security
418444

419-
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.
420-
421-
<AppOnly>
445+
[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.
422446

423-
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`.
424-
425-
</AppOnly>
426-
427-
<PagesOnly>
428-
429-
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`.
430-
431-
</PagesOnly>
447+
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`.
432448

433449
```js
434450
{
@@ -439,7 +455,9 @@ If you're deploying to [Vercel](https://vercel.com/docs/concepts/edge-network/he
439455

440456
### X-Frame-Options
441457

442-
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.
458+
[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.
459+
460+
**This header has been superseded by CSP's `frame-ancestors` option**, which has better support in modern browsers.
443461

444462
```js
445463
{
@@ -450,7 +468,7 @@ This header indicates whether the site should be allowed to be displayed within
450468

451469
### Permissions-Policy
452470

453-
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).
471+
[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`.
454472

455473
```js
456474
{
@@ -461,7 +479,9 @@ This header allows you to control which features and APIs can be used in the bro
461479

462480
### X-Content-Type-Options
463481

464-
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`.
482+
[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.
483+
484+
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`.
465485

466486
```js
467487
{
@@ -472,7 +492,7 @@ This header prevents the browser from attempting to guess the type of content if
472492

473493
### Referrer-Policy
474494

475-
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/).
495+
[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.
476496

477497
```js
478498
{
@@ -483,35 +503,7 @@ This header controls how much information the browser includes when navigating f
483503

484504
### Content-Security-Policy
485505

486-
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.
487-
488-
You can read about the many different CSP options [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP).
489-
490-
You can add Content Security Policy directives using a template string.
491-
492-
```js
493-
// Before defining your Security Headers
494-
// add Content Security Policy directives using a template string.
495-
496-
const ContentSecurityPolicy = `
497-
default-src 'self';
498-
script-src 'self';
499-
child-src example.com;
500-
style-src 'self' example.com;
501-
font-src 'self';
502-
`
503-
```
504-
505-
When a directive uses a keyword such as `self`, wrap it in single quotes `''`.
506-
507-
In the header's value, replace the new line with a space.
508-
509-
```js
510-
{
511-
key: 'Content-Security-Policy',
512-
value: ContentSecurityPolicy.replace(/\s{2,}/g, ' ').trim()
513-
}
514-
```
506+
Learn more about adding a [Content Security Policy](/docs/app/building-your-application/configuring/content-security-policy) to your application.
515507

516508
## Version History
517509

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
title: Content Security Policy
3+
description: Learn how to set a Content Security Policy (CSP) for your Next.js application.
4+
source: app/building-your-application/configuring/content-security-policy
5+
---
6+
7+
{/* 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. */}

docs/03-pages/01-building-your-application/07-deploying/01-production-checklist.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Before taking your Next.js application to production, here are some recommendati
2222
- [Automatic Font Optimization](/docs/pages/building-your-application/optimizing/fonts)
2323
- [Script Optimization](/docs/pages/building-your-application/optimizing/scripts)
2424
- Improve [loading performance](#loading-performance)
25+
- Consider adding a [Content Security Policy](/docs/pages/building-your-application/configuring/content-security-policy)
2526

2627
## Caching
2728

0 commit comments

Comments
 (0)