# 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 appliquer ce nonce à chaque script qu'il rend pour vous.

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 les nonces sont nouveaux pour vous, [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 livre `proxy.ts`, donc c'est ce que ce billet utilise.

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 une valeur fraîche et indevinable à chaque requête depuis une source cryptographiquement sûre, ce qui est exactement ce dont un nonce a besoin. Les exigences plus profondes (pourquoi il doit être par requête et issu d'une source aléatoire sûre) sont dans [démarrer avec Content Security Policy](/fr/blog/get-started-with-csp), donc ce billet ne les redémontre 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. La définir sur la requête (`requestHeaders.set('Content-Security-Policy', ...)`) est la façon dont Next.js voit le nonce et l'applique aux scripts qu'il rend. La définir sur la réponse (`response.headers.set('Content-Security-Policy', ...)`) est ce que le navigateur applique réellement. Vous avez besoin des deux.

Le header `x-nonce` est un 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 chaîne en code 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 existe 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 livre 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 est tout le câblage. 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é ruine l'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 ne touchent autrement 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` que Next.js ne nonce pas automatiquement.

Lisez-le depuis les headers de la requête dans un Server Component. Notez que `headers()` est asynchrone dans le Next.js actuel, donc vous l'attendez 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 le script est approuvé par la politique.

## 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
```

L'App Router est le focus de ce billet, donc voici la 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 depuis le 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 le [é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/).
