Docs

Plugin Next.js

Headers de sécurité et monitoring des dépendances pour Next.js. Un wrapper, zéro config.

Installation

Installez le plugin avec votre gestionnaire de paquets :

npm install @withpanache/nextjs
# ou
pnpm add @withpanache/nextjs
# ou
yarn add @withpanache/nextjs

Démarrage rapide

Wrappez votre config Next.js avec withPanache. C'est tout : votre app a maintenant des headers de sécurité et X-Powered-By est désactivé.

// next.config.ts
import { withPanache } from "@withpanache/nextjs"

export default withPanache({
  // votre config Next.js existante
})

Headers de sécurité

Par défaut, withPanache injecte les headers suivants sur toutes les routes. Ils sont assez permissifs pour la plupart des apps Next.js tout en passant les checks de sécurité Panache.

HeaderValeur par défaut
Strict-Transport-Securitymax-age=63072000; includeSubDomains
Content-Security-Policydefault-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'self'
X-Content-Type-Optionsnosniff
X-Frame-OptionsSAMEORIGIN
Referrer-Policystrict-origin-when-cross-origin
Permissions-Policycamera=(), microphone=(), geolocation=()

CSP dynamique au runtime

Next.js fige la fonction headers() dans le manifest au moment du next build. Si une directive CSP dépend d'une variable d'environnement qui n'existe qu'au déploiement (un domaine CDN, une origine de reports, une valeur par tenant), un CSP build-time n'aura pas cette source : le header est capturé avant que la variable soit définie, et la policy servie perd silencieusement l'origine. La solution est d'émettre le CSP depuis votre middleware (proxy.ts), là où l'env runtime est disponible. Trois étapes : 1. Coupez le CSP build-time dans withPanache avec contentSecurityPolicy: false, et coupez le reporting CSP build-time avec cspMonitoring: false. Les headers statiques (HSTS, X-Frame-Options, Referrer-Policy, Permissions-Policy, X-Content-Type-Options) restent au build, seul le CSP est libéré. 2. Votre middleware devient le propriétaire unique du header Content-Security-Policy. 3. Importez buildCspHeaders et setSecurityHeaders depuis @withpanache/nextjs/headers. Ce sous-chemin est Edge-safe (zéro import node:). Construisez la liste une seule fois au chargement du module : rien ne varie par requête, donc lire process.env là est correct et évite de reconstruire à chaque requête. setSecurityHeaders utilise Headers.set, garantissant un seul header CSP.

// next.config.ts
import { withPanache } from "@withpanache/nextjs"

export default withPanache(nextConfig, {
  // Garde les headers de securite statiques au build, mais libere le CSP
  // pour que le middleware l'emette depuis l'env runtime.
  security: { contentSecurityPolicy: false },
  // Coupe aussi l'injection du reporting CSP build-time, sinon un second
  // header Content-Security-Policy serait emis au build.
  cspMonitoring: false,
})

// proxy.ts (ou middleware.ts)
import { buildCspHeaders, setSecurityHeaders } from "@withpanache/nextjs/headers"
import { NextResponse } from "next/server"
import type { NextRequest } from "next/server"

// Fige une seule fois au chargement du module : rien ne varie par requete,
// donc REPORTS_DOMAIN (une env runtime, vide au build) est lu ici, pas au
// next build.
const CSP_HEADERS = buildCspHeaders({
  dev: process.env.NODE_ENV !== "production",
  contentSecurityPolicy: {
    "frame-src": ["'self'", process.env.REPORTS_DOMAIN],
  },
})

export default function proxy(request: NextRequest) {
  const response = NextResponse.next()
  // .set garantit un seul header Content-Security-Policy.
  setSecurityHeaders(response.headers, CSP_HEADERS)
  return response
}

Personnaliser les headers

Passez un objet security pour modifier ou désactiver des headers individuels. Mettez un header à false pour le désactiver.

export default withPanache(nextConfig, {
  security: {
    // Desactiver HSTS (ex. derriere un reverse proxy)
    strictTransportSecurity: false,

    // Politique de referrer personnalisee
    referrerPolicy: "no-referrer",

    // Framing plus strict
    xFrameOptions: "DENY",
  },
})

Content Security Policy

Le CSP peut être configuré comme un objet typé ou une chaîne brute. Forme objet : vos sources sont ajoutées aux valeurs par défaut (dédupliquées). Pas besoin de répéter 'self' ou les autres valeurs par défaut. Les directives que vous ne mentionnez pas gardent leurs valeurs par défaut. Pour remplacer une directive spécifique au lieu de l'étendre, utilisez le helper replace(). Forme chaîne : remplace entièrement le CSP par défaut. Toutes les formes bénéficient de l'autocomplétion TypeScript pour les noms de directives et les valeurs sources.

import { withPanache, replace } from "@withpanache/nextjs"

export default withPanache(nextConfig, {
  security: {
    contentSecurityPolicy: {
      // Etend : les sources s'ajoutent aux defaults
      "script-src": ["https://cdn.example.com"],
      // Remplace : cette directive ecrase entierement le default
      "connect-src": replace(["'self'", "https://api.example.com"]),
    },
  },
})

// Forme chaine : remplace tout le CSP
export default withPanache(nextConfig, {
  security: {
    contentSecurityPolicy: "default-src 'none'; script-src 'self'",
  },
})

Monitoring CSP

Reporting optionnel des violations CSP côté client. Générez un token CSP dans le dashboard (Settings > CSP) et le plugin va : 1. Ajouter `report-to panache` à votre header Content-Security-Policy (avec un fallback `report-uri` legacy pour Safari ≤17 et les versions anciennes de Firefox). 2. Émettre un header `Reporting-Endpoints: panache="…"` pour que les navigateurs envoient les violations directement à Panache. Les rapports sont agrégés à l'heure et visibles dans le dashboard. Le monitoring CSP est désactivé si aucun token n'est fourni. Le mode learning bascule le header `Content-Security-Policy` (bloquant) en `Content-Security-Policy-Report-Only` : les violations sont rapportées mais pas bloquées. Utile pour découvrir ce que charge votre site avant d'activer le blocage. Les rapports issus des branches non-défaut sont tagués avec le nom de branche (détecté via CI_COMMIT_BRANCH, GITHUB_REF_NAME, VERCEL_GIT_COMMIT_REF, CF_PAGES_BRANCH, ou git local). Définissez `defaultBranch` si votre projet n'utilise pas `main`. Pour les déploiements Panache self-hosted, définissez `cspMonitoring.endpoint` (ou PANACHE_CSP_ENDPOINT) pour pointer vers votre propre URL d'ingestion. Default : https://csp.withpanache.dev

// Recommande : variables d'environnement
// PANACHE_CSP_TOKEN=<token-csp-depuis-le-dashboard>
// PANACHE_CSP_LEARNING_MODE=1   # optionnel, bascule en Report-Only

// Ou via les options du plugin
export default withPanache(nextConfig, {
  cspMonitoring: {
    token: process.env.PANACHE_CSP_TOKEN,
    learningMode: false,
    // endpoint: "https://csp.your-self-hosted.example", // optionnel
  },
  defaultBranch: "main", // override si votre branche par defaut n'est pas "main"
})

Permissions Policy

Même principe que le CSP. La forme objet ajoute aux valeurs par défaut, la forme chaîne remplace. Un tableau vide désactive la fonctionnalité.

export default withPanache(nextConfig, {
  security: {
    permissionsPolicy: {
      // Fusion avec les defaults (camera, micro, geoloc restent desactives)
      fullscreen: ["self"],
      payment: [],
    },
  },
})

Comportement de fusion

Si vous définissez déjà une fonction headers() dans votre config Next.js, Panache ajoute ses headers à votre route catch-all sans écraser ceux que vous avez définis. Si vous n'avez pas de fonction headers(), Panache en crée une. Vos headers ont toujours la priorité. Panache n'écrase jamais un header existant. En développement, le CSP par défaut inclut 'unsafe-eval' dans script-src car le mode dev de React en a besoin. Ceci est automatiquement retiré en production.

Monitoring des dépendances

Pendant next build, le plugin détecte votre gestionnaire de paquets, collecte l'arbre complet des dépendances et l'envoie à Panache pour le suivi des vulnérabilités. Cela s'exécute dans un sous-processus détaché et ne bloque ni ne ralentit jamais votre build. En cas d'échec, il échoue silencieusement. Gestionnaires supportés : pnpm, npm, yarn (classic et berry). Pour l'activer, ajoutez votre token de site :

export default withPanache(
  { /* next config */ },
  { token: process.env.PANACHE_SITE_TOKEN }
)

Détection CI/CD

Le plugin détecte automatiquement le SHA git, la branche et l'URL de preview depuis les environnements CI courants : SHA Git : GITHUB_SHA, CI_COMMIT_SHA, VERCEL_GIT_COMMIT_SHA, CF_PAGES_COMMIT_SHA, ou git local. Branche : GITHUB_REF_NAME, CI_COMMIT_BRANCH, VERCEL_GIT_COMMIT_REF, CF_PAGES_BRANCH, ou git local. URL Preview : VERCEL_URL, CF_PAGES_URL, DEPLOY_URL. Aucune configuration nécessaire. Si les variables d'environnement sont présentes, elles sont utilisées automatiquement.

Toutes les options

Référence complète des options :

interface PanacheOptions {
  /** Token de site depuis le dashboard Panache. Lit aussi l'env PANACHE_SITE_TOKEN. */
  token?: string
  /** URL de l'API d'ingestion. Default: "https://withpanache.dev/api/v1/ingest" */
  apiUrl?: string
  /** Headers de securite. true = tous les defaults, false = aucun, objet = personnaliser. Default: true */
  security?: boolean | SecurityHeadersConfig
  /** Branche par defaut. Pilote les features branch-aware (rapports CSP par branche, etc.). Default: "main" */
  defaultBranch?: string
  /** Reporting des violations CSP. Desactive si aucun token n'est fourni. */
  cspMonitoring?: {
    /** Token CSP depuis le dashboard. Lit aussi PANACHE_CSP_TOKEN. */
    token?: string
    /** Bascule en Content-Security-Policy-Report-Only. Lit aussi PANACHE_CSP_LEARNING_MODE. Default: false */
    learningMode?: boolean
    /** Override de l'URL de base du report endpoint. Lit aussi PANACHE_CSP_ENDPOINT. */
    endpoint?: string
  }
}

TypeScript

Tous les types sont exportés pour utilisation dans votre config :

import type {
  PanacheOptions,
  SecurityHeadersConfig,
  CspDirectives,
  CspDirectiveName,
  CspSourceValue,
  CspMonitoringConfig,
  PermissionsPolicyDirectives,
  PermissionsPolicyFeature,
  ReferrerPolicy,
} from "@withpanache/nextjs"

Besoin d'aide ?

Pour toute question sur le plugin ou si vous avez besoin d'assistance, contactez-nous à hello@withpanache.dev