# Mettre en place un nonce CSP dans Next.js (/fr/blog/csp-nonce-nextjs)



Vous pouvez donner à une application Next.js App Router une Content Security Policy (CSP) stricte, basée sur un nonce, dans un seul fichier. Une CSP est un header de réponse HTTP qui indique au navigateur quels scripts et autres ressources une page peut charger et exécuter. La façon propre de procéder dans Next.js consiste à générer un nouveau nonce par requête dans `proxy.ts`, à définir la politique à la fois sur la requête et sur la réponse, et à laisser Next.js poser ce nonce sur chaque script qu'il rend.

Ce guide pratique cible le Next.js actuel (16). Il utilise Web Crypto pour que le code tourne dans le runtime Edge, définit un seul [`script-src`](/fr/docs/web-security/policies/content-security-policy/directives/script-src) strict avec un [nonce](/fr/docs/web-security/policies/content-security-policy/values/csp-hashes-nonce) et [`'strict-dynamic'`](/fr/docs/web-security/policies/content-security-policy/values/csp-keywords), et montre comment lire le nonce quand vous avez votre propre script inline ou une balise `next/script`. Si vous découvrez les nonces, [démarrer avec Content Security Policy](/fr/blog/get-started-with-csp) explique ce qu'est un nonce et pourquoi il fonctionne.

## La version courte [#la-version-courte]

1. Générez un nonce par requête dans `proxy.ts` avec Web Crypto.
2. Définissez la CSP sur les headers de la requête transmise (pour que Next.js puisse lire le nonce) et sur la réponse (pour que le navigateur l'applique).
3. Laissez Next.js ajouter automatiquement le nonce aux scripts qu'il rend. Vous ne mettez pas vous-même le nonce sur ses balises de script.
4. Forcez le rendu dynamique sur toute route qui utilise le nonce. Un nonce par requête et l'optimisation statique sont incompatibles.
5. Lisez le nonce depuis `headers()` dans un Server Component uniquement quand vous avez votre propre script inline ou une balise `next/script`.

## Une note de version avant le code [#une-note-de-version-avant-le-code]

Dans Next.js 16, la convention de fichier du middleware a été renommée. Le fichier est désormais `proxy.ts` et la fonction exportée est `proxy`. Le guide CSP officiel utilise `proxy.ts`, c'est donc ce que ce billet reprend.

Sur Next.js 15 et antérieur, ce fichier est `middleware.ts` avec `export function middleware`. Le corps est identique, seuls le nom du fichier et le nom de la fonction diffèrent.

## Étape 1, générer le nonce dans proxy.ts [#étape-1-générer-le-nonce-dans-proxyts]

Le proxy tourne dans le runtime Edge, donc utilisez Web Crypto. C'est le détail qui fait trébucher : le `crypto.randomBytes` de Node et `require('crypto')` ne sont pas disponibles dans le runtime Edge, donc les appeler lève une erreur. Utilisez `crypto.randomUUID()`, qui fait partie de Web Crypto et y est disponible :

```ts
const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
```

Un nonce doit être unique par requête, imprévisible, et en base64. `crypto.randomUUID()` vous donne à chaque requête une valeur neuve, impossible à deviner, issue d'une source cryptographiquement sûre, exactement ce qu'attend un nonce. Le détail des exigences (pourquoi il doit être régénéré à chaque requête et venir d'une source aléatoire sûre) est dans [démarrer avec Content Security Policy](/fr/blog/get-started-with-csp), ce billet ne le reprend pas.

## Étape 2, écrire le proxy [#étape-2-écrire-le-proxy]

Voici le proxy actuel complet. Il construit une politique stricte, y inscrit le nonce, et définit la politique à deux endroits :

```ts
// proxy.ts
import { NextRequest, NextResponse } from 'next/server'

export function proxy(request: NextRequest) {
  const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
  const isDev = process.env.NODE_ENV === 'development'
  const cspHeader = `
    default-src 'self';
    script-src 'self' 'nonce-${nonce}' 'strict-dynamic'${isDev ? " 'unsafe-eval'" : ''};
    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';
    upgrade-insecure-requests;
  `
  const contentSecurityPolicyHeaderValue = cspHeader.replace(/\s{2,}/g, ' ').trim()

  const requestHeaders = new Headers(request.headers)
  requestHeaders.set('x-nonce', nonce)
  requestHeaders.set('Content-Security-Policy', contentSecurityPolicyHeaderValue)

  const response = NextResponse.next({ request: { headers: requestHeaders } })
  response.headers.set('Content-Security-Policy', contentSecurityPolicyHeaderValue)
  return response
}
```

Deux choses dans ce code font le vrai travail.

La politique est définie à la fois sur les headers de la requête transmise et sur la réponse. C'est en la définissant sur la requête (`requestHeaders.set('Content-Security-Policy', ...)`) que Next.js voit le nonce et le pose sur les scripts qu'il rend. Celle définie sur la réponse (`response.headers.set('Content-Security-Policy', ...)`) est celle que le navigateur applique réellement. Il vous faut les deux.

Le header `x-nonce` est là pour le confort. Le proxy place le nonce brut sur un header de requête personnalisé pour que vos propres composants puissent le relire plus tard sans analyser la chaîne de la CSP.

`'unsafe-eval'` n'est ajouté qu'en développement. React utilise `eval` en développement, donc sans lui le serveur de dev casse sous la politique. La vérification `isDev` garde [`'unsafe-eval'`](/fr/docs/web-security/policies/content-security-policy/values/csp-keywords) entièrement hors de la production, où il réactiverait l'exécution de code à partir de chaînes et affaiblirait la politique.

La politique ici s'appuie sur `'strict-dynamic'` plutôt que sur des listes d'hôtes autorisés pour les scripts. `'strict-dynamic'` indique au navigateur de faire confiance aux scripts qui portent le nonce, ainsi qu'à tout script que ceux-ci chargent, et d'ignorer les listes d'hôtes pour les scripts. C'est ce qui rend une politique à nonce solide : un attaquant qui injecte une balise de script ne peut pas deviner le nonce, donc elle ne s'exécute jamais. Évitez de recourir à [`'unsafe-inline'`](/fr/blog/unsafe-inline-csp) pour faire taire les erreurs de script ; il réactive exactement l'exécution inline que la CSP est là pour bloquer, et le navigateur l'ignore de toute façon dès qu'un nonce est présent.

### Cadrez le proxy pour qu'il ignore les assets statiques [#cadrez-le-proxy-pour-quil-ignore-les-assets-statiques]

Vous ne voulez généralement pas que le proxy génère un nonce pour les fichiers statiques et les requêtes de prefetch. Ajoutez un `config.matcher` pour le cadrer sur les chemins qui rendent du HTML. Le guide officiel fournit un matcher qui ignore les internes de Next.js, les fichiers statiques et les prefetches ; gardez-en un pour que le proxy ne tourne que là où le nonce est nécessaire.

## Étape 3, laisser Next.js ajouter le nonce à ses propres scripts [#étape-3-laisser-nextjs-ajouter-le-nonce-à-ses-propres-scripts]

C'est la partie qui rend Next.js agréable à utiliser. Next.js lit le nonce depuis le header `Content-Security-Policy` de la requête et l'applique automatiquement aux scripts qu'il rend. Cela couvre les scripts du framework, vos bundles de page, les scripts et styles inline que Next.js génère, et tout composant `<Script nonce>`.

Vous n'ajoutez donc pas manuellement un attribut `nonce` aux propres balises de script de Next.js. Définir la politique sur le header de requête à l'étape 2 suffit. Next.js fait le reste.

## Étape 4, forcer le rendu dynamique sur les routes qui utilisent le nonce [#étape-4-forcer-le-rendu-dynamique-sur-les-routes-qui-utilisent-le-nonce]

Un nonce par requête n'a de sens que si chaque requête obtient son propre HTML rendu. Cela signifie que la route doit se rendre dynamiquement. L'optimisation statique, l'Incremental Static Regeneration (ISR) et le Partial Prerendering (PPR) sont incompatibles avec une CSP basée sur un nonce, parce qu'ils réutilisent une réponse pré-rendue sur plusieurs requêtes, et un nonce réutilisé n'a plus aucun intérêt.

Forcez le rendu dynamique avec `await connection()` dans la page :

```tsx
import { connection } from 'next/server'

export default async function Page() {
  await connection()
  // ...
}
```

Lire `headers()` dans la route (étape 5) la fait aussi passer en rendu dynamique, donc si vous y lisez déjà le nonce, vous n'aurez peut-être pas besoin de `connection()` en plus. Utilisez `connection()` pour les routes qui ont besoin du rendu dynamique mais qui, autrement, ne touchent pas aux données de la requête.

## Étape 5, lire le nonce pour votre propre script inline [#étape-5-lire-le-nonce-pour-votre-propre-script-inline]

La plupart du temps, vous n'avez pas à toucher au nonce, parce que Next.js l'applique pour vous. Vous ne le lisez que quand vous avez votre propre script inline ou une balise `next/script` à laquelle Next.js n'ajoute pas le nonce automatiquement.

Lisez-le depuis les headers de la requête dans un Server Component. Notez que `headers()` est asynchrone dans le Next.js actuel, il faut donc l'attendre avec `await` :

```tsx
import { headers } from 'next/headers'
import Script from 'next/script'

export default async function Page() {
  const nonce = (await headers()).get('x-nonce')
  return (
    <Script
      src="https://example.com/script.js"
      strategy="afterInteractive"
      nonce={nonce}
    />
  )
}
```

Le header `x-nonce` est celui que le proxy a défini à l'étape 2. Passez cette valeur à la prop `nonce` et la politique accepte le script.

## Pages Router [#pages-router]

Si vous êtes encore sur le Pages Router, le proxy et le flux du nonce sont les mêmes. Ce qui diffère, c'est la façon de lire le nonce, parce qu'il n'y a pas de `next/headers`.

Dans une page, lisez-le dans `getServerSideProps` depuis les headers de la requête et passez-le en prop :

```tsx
export async function getServerSideProps({ req }) {
  const nonce = req.headers['x-nonce'] ?? ''
  return { props: { nonce } }
}
```

Pour ajouter le nonce aux scripts au niveau du document, lisez-le dans `_document.tsx` et appliquez-le à `<Head>` et `<NextScript>` :

```tsx
import Document, { Head, Html, Main, NextScript } from 'next/document'

class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const initialProps = await Document.getInitialProps(ctx)
    const nonce = ctx.req?.headers?.['x-nonce'] ?? ''
    return { ...initialProps, nonce }
  }

  render() {
    const { nonce } = this.props as { nonce: string }
    return (
      <Html>
        <Head nonce={nonce} />
        <body>
          <Main />
          <NextScript nonce={nonce} />
        </body>
      </Html>
    )
  }
}

export default MyDocument
```

Ce billet porte surtout sur l'App Router, d'où cette version courte. Le proxy de l'étape 2 reste inchangé.

## Testez d'abord en Report-Only, puis surveillez les reports [#testez-dabord-en-report-only-puis-surveillez-les-reports]

Déployez la politique sur le header `Content-Security-Policy-Report-Only` avant de l'appliquer. En mode report-only, le navigateur ne bloque rien et signale seulement ce que la politique aurait bloqué, donc un script tiers oublié ne peut pas casser la page pendant que vous l'ajustez. Pointez la politique vers un endpoint de reporting et collectez les reports du trafic réel :

```http
Reporting-Endpoints: csp-endpoint="https://<Endpoint-ID>.report.centralcsp.com"
```

```http
Content-Security-Policy-Report-Only:
  default-src 'self';
  script-src 'self' 'nonce-r4nd0mBase64Value' 'strict-dynamic';
  style-src 'self' 'nonce-r4nd0mBase64Value';
  object-src 'none';
  base-uri 'self';
  frame-ancestors 'none';
  report-to csp-endpoint
```

Pour définir ceci sur le header report-only dans Next.js, remplacez `Content-Security-Policy` par `Content-Security-Policy-Report-Only` dans le proxy, ajoutez un header `Reporting-Endpoints`, et ajoutez `report-to csp-endpoint` à la chaîne de la politique.

[CentralCSP](/platform/csp-builder) collecte ces reports de violation en Report-Only, les regroupe par directive et par origine, et montre les scripts qui tournent sur chaque page, pour que vous voyiez exactement de quel tiers une balise a besoin avant d'appliquer. Vous pouvez [démarrer un essai gratuit](/register), pointer un header Report-Only vers lui, et regarder les reports arriver. Pour évaluer la politique finie à la recherche de sources faibles ou d'un `object-src` manquant, passez-la dans l'[évaluateur CSP](/tools/csp-evaluator), et pour vérifier ce qu'un site en ligne envoie déjà, utilisez le [scanner CSP](/tools/csp-scanner). Le workflow complet Report-Only d'abord, sur tous les frameworks, est dans [comment construire une CSP solide](/fr/blog/how-to-build-a-strong-csp).

Faire tourner Google Tag Manager ou GA4 dans votre app Next.js sous cette politique fonctionne de la même manière : ajoutez le nonce au bootstrap GTM et laissez `'strict-dynamic'` faire confiance aux tags. Voyez [CSP avec Google Analytics et Tag Manager](/fr/blog/csp-google-analytics-tag-manager) pour le snippet exact et l'endroit où placer les hôtes Google.

## Questions fréquentes [#questions-fréquentes]

### Comment ajouter un nonce CSP dans Next.js ? [#comment-ajouter-un-nonce-csp-dans-nextjs-]

Générez un nonce par requête dans `proxy.ts` avec `Buffer.from(crypto.randomUUID()).toString('base64')`, définissez le header `Content-Security-Policy` à la fois sur la requête transmise et sur la réponse, et incluez `'nonce-...'` et `'strict-dynamic'` dans `script-src`. Next.js applique ensuite automatiquement le nonce aux scripts qu'il rend.

### Pourquoi mon nonce lève-t-il une erreur dans le middleware ou le proxy Next.js ? [#pourquoi-mon-nonce-lève-t-il-une-erreur-dans-le-middleware-ou-le-proxy-nextjs-]

Parce que vous utilisez le `crypto.randomBytes` de Node ou `require('crypto')`, qui ne sont pas disponibles dans le runtime Edge où tourne le proxy. Utilisez Web Crypto à la place : `crypto.randomUUID()`.

### Dois-je ajouter un nonce à chaque balise de script dans Next.js ? [#dois-je-ajouter-un-nonce-à-chaque-balise-de-script-dans-nextjs-]

Non. Next.js lit le nonce depuis le header `Content-Security-Policy` de la requête et l'applique aux scripts qu'il rend, y compris les scripts du framework, les bundles de page, et les composants `<Script nonce>`. Vous ne lisez le nonce vous-même que pour votre propre script inline ou une balise `next/script` que vous contrôlez.

### Pourquoi un nonce casse-t-il mes pages statiques dans Next.js ? [#pourquoi-un-nonce-casse-t-il-mes-pages-statiques-dans-nextjs-]

Un nonce par requête exige le rendu dynamique, il est donc incompatible avec l'optimisation statique, l'ISR et le Partial Prerendering. Forcez le rendu dynamique avec `await connection()`, ou lisez `headers()` dans la route, ce qui la fait aussi passer en rendu dynamique.

### Est-ce différent sur Next.js 15 ? [#est-ce-différent-sur-nextjs-15-]

Seulement le nom du fichier. Sur Next.js 15 et antérieur, le fichier est `middleware.ts` avec `export function middleware`. La génération du nonce, le double header requête et réponse, et le comportement d'auto-nonce sont identiques.

## À retenir [#à-retenir]

Une CSP stricte basée sur un nonce dans Next.js se résume à un seul proxy : générez le nonce avec Web Crypto, définissez la politique à la fois sur la requête et sur la réponse, et laissez Next.js ajouter le nonce à ses propres scripts. Forcez le rendu dynamique sur les routes qui l'utilisent, lisez le nonce depuis `headers()` uniquement pour vos propres scripts inline, et déployez le tout d'abord en Report-Only pour que rien ne casse pendant que vous l'ajustez.

Pour aller plus loin : le [guide CSP de Next.js](https://nextjs.org/docs/app/guides/content-security-policy), le [guide CSP de MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP), et la [spécification W3C CSP Level 3](https://www.w3.org/TR/CSP3/).
