Presque tous les trous d'une appli Next.js construite avec l'IA viennent d'une seule confusion : ne pas savoir quelle partie du code tourne sur le serveur et quelle partie finit dans le navigateur. Next.js mélange les deux dans le même projet, et Cursor, v0 ou Bolt franchissent la frontière sans prévenir. Ce guide explique ce modèle mental en cinq minutes, passe en revue ce que les outils d'IA font mal dans Next.js, te laisse un bloc de règles à coller dans ton projet, un prompt de revue et quatre vérifications de l'extérieur. La liste point par point de ce qu'il faut corriger est dans la checklist Next.js avant le lancement ; ici, c'est le pourquoi.
Partie 1 : où tourne chaque chose
Cinq endroits, deux mondes
Dans l'App Router, ton code peut vivre à cinq endroits : les Server Components (serveur uniquement, n'atteignent jamais le navigateur), les Client Components (marqués "use client", envoyés entiers au navigateur), les Route Handlers (app/api/.../route.ts, des URL publiques), les Server Actions (des fonctions avec "use server" que le client invoque en POST) et le middleware (appelé proxy dans les versions les plus récentes), qui tourne avant chaque requête. Les trois derniers plus les Server Components forment le monde du serveur ; les Client Components, le monde du navigateur. Tout ce que tu importes dans un Client Component, constantes et fonctions utilitaires comprises, voyage dans le bundle.
Ce qui atteint le navigateur
- Les variables d'environnement préfixées
NEXT_PUBLIC_, incrustées au moment du build. Les autres n'existent que sur le serveur. - Toute donnée qu'un Server Component passe en prop à un Client Component. Si tu passes l'objet
userentier, il voyage avec le hash du mot de passe et l'e-mail. - Les fichiers du dossier
public/, tels quels. - Les source maps, si tu les actives avec
productionBrowserSourceMaps.
Trois mécanismes qui évitent de franchir la frontière
Le paquet server-only : un import 'server-only' en tête d'un fichier fait échouer le build si quelqu'un l'importe depuis un Client Component. Utilise-le dans tout ce qui touche à la base de données ou lit des secrets. Une couche d'accès aux données : un module qui centralise la vérification de session et ne renvoie que les champs nécessaires (un DTO), au lieu d'interroger la base depuis chaque page. Et la règle que chaque point d'entrée vérifie la session : Route Handlers, Server Actions et Server Components qui lisent des données privées, chacun de son côté. Le middleware ne remplace pas cette vérification, comme l'a montré la CVE-2025-29927.
Ce que Next.js ne fait pas pour toi
Il n'ajoute pas d'en-têtes de sécurité, ne limite pas les requêtes, ne valide pas l'entrée des Server Actions et ne protège pas des routes parce qu'elles sont dans un dossier nommé (admin). Tout ça, c'est ton code ou la configuration dans next.config.
Partie 2 : ce que l'IA fait mal dans Next.js
- Tout en "use client". Quand un composant a besoin d'état, l'IA le marque client et entraîne avec lui la requête à la base ou la clé d'API. Sépare : le composant serveur récupère les données, le composant client ne fait qu'afficher.
- Une protection seulement dans le middleware. Le middleware redirige s'il n'y a pas de cookie et le handler lui fait confiance. Vérifie la session aussi dans le handler ; c'est ce que la CVE du middleware a appris, et les versions antérieures à 15.2.3 ou 14.2.25 restent vulnérables.
- Des Route Handlers et des Server Actions sans authentification ni validation. Une Server Action est un endpoint public même sans URL visible. Vérifie qui appelle et valide le corps avec un schéma (zod ou équivalent) avant de toucher à quoi que ce soit.
- Des secrets avec le préfixe public.
NEXT_PUBLIC_OPENAI_KEYparce que le composant client « en avait besoin ». La clé va dans un Route Handler et le client appelle le handler. - remotePatterns avec un joker.
hostname: '**'dans la configuration des images transforme ton optimiseur d'images en proxy ouvert vers n'importe quelle URL. - Des redirections avec paramètre.
redirect(searchParams.next)sans vérifier que la destination est un chemin interne : du phishing avec ton domaine. - Des fetch vers des URL de l'utilisateur. Un handler qui télécharge ce qu'on lui passe peut atteindre des services internes de ton réseau ou de ton hébergeur.
- dangerouslySetInnerHTML avec du contenu utilisateur. XSS classique ; si tu as besoin de HTML, assainis-le côté serveur.
- Ni en-têtes ni limites. Pas de CSP, pas de HSTS, pas de X-Frame-Options, et un endpoint qui appelle un modèle payant sans plafond par utilisateur.
Un bloc de règles pour ton projet
Colle-le dans le fichier de règles que lit ton outil (.cursor/rules, AGENTS.md ou l'équivalent) pour qu'il s'applique à chaque génération :
Règles de sécurité pour ce projet Next.js :
- Les composants sont des Server Components sauf s'ils ont besoin d'état ou d'événements du navigateur. Un Client Component n'importe jamais de modules qui accèdent à la base de données ou lisent process.env sans préfixe.
- Tout module qui touche à la base de données ou aux secrets commence par import 'server-only'.
- N'utilise jamais NEXT_PUBLIC_ pour une clé secrète. Les appels à des services avec clé vont dans un Route Handler ou une Server Action.
- Chaque Route Handler et chaque Server Action vérifie la session et valide l'entrée avec un schéma avant de faire quoi que ce soit. Le middleware ne compte pas comme authentification.
- Ne passe aux Client Components que les champs nécessaires, jamais l'objet entier de l'utilisateur ou de la ligne.
- Les redirections n'acceptent que des chemins internes commençant par une seule barre oblique.
- images.remotePatterns liste des domaines précis, jamais un joker d'hôte.
- N'utilise pas dangerouslySetInnerHTML avec du contenu utilisateur.
- next.config définit les en-têtes de sécurité et n'active pas productionBrowserSourceMaps.
Un prompt de revue avant de déployer
Passe en revue la sécurité de cette appli Next.js et ne change rien pour l'instant. Réponds avec une liste :
1. Client Components qui importent des modules de base de données, lisent des secrets ou reçoivent en prop des objets avec des champs sensibles.
2. Route Handlers et Server Actions sans vérification de session ou sans validation d'entrée.
3. Routes dont la seule protection est le middleware.
4. Variables NEXT_PUBLIC_ contenant des clés secrètes.
5. Redirections acceptant une destination fournie par l'utilisateur, fetch vers des URL de l'utilisateur et usages de dangerouslySetInnerHTML.
6. remotePatterns avec joker, productionBrowserSourceMaps activé et en-têtes de sécurité absents de next.config.
7. Version de Next.js et si elle est antérieure à 15.2.3 ou 14.2.25.
Pour chaque point, propose le changement exact et attends ma confirmation.
Vérifie le résultat de l'extérieur
Quatre commandes contre ton domaine. Les en-têtes et la version du middleware :
curl -sI https://tondomaine.com | grep -iE "strict-transport|content-security|x-frame|x-content-type"
curl -s -o /dev/null -w "%{http_code}\n" -H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware" https://tondomaine.com/dashboard
Si la seconde renvoie 200 sur une route qui devrait rediriger vers le login, ta version est vulnérable à la CVE du middleware : mets à jour aujourd'hui. Ensuite, les source maps et les routes d'API que le bundle révèle :
curl -s https://tondomaine.com | grep -oE '/_next/static/chunks/[^"]+\.js' | head -3
curl -s https://tondomaine.com/_next/static/chunks/main-app-XXXX.js | grep -oE '"/api/[^"]+"' | sort -u
Demande chaque route d'API sans session : la bonne réponse est un 401 ou un 403. Scanaris fait ces vérifications et une quarantaine d'autres en 30 secondes : il détecte que l'appli est en Next.js et sa version, teste l'en-tête de la CVE, cherche des secrets dans les chunks, vérifie si les source maps sont publiées, contrôle en-têtes, CSP, cookies, CORS et fichiers exposés, et te donne un fix prompt pour chaque échec à coller dans Cursor ou v0. Si tu déploies sur Vercel, le guide Vercel couvre les previews publiques et les variables d'environnement.
Questions fréquentes
Les Server Actions sont-elles sûres par défaut ?
Elles sont plus difficiles à découvrir qu'un Route Handler, mais tout aussi publiques : quiconque a l'identifiant de l'action peut l'invoquer. Traite-les comme un endpoint : session et validation dans la fonction.
Le middleware suffit-il pour protéger le tableau de bord ?
Non. Il sert à rediriger vite, mais la vraie vérification va dans chaque page, handler et action. La CVE-2025-29927 permettait de contourner le middleware avec un en-tête ; même sur une version corrigée, la règle reste la même.
Puis-je continuer à utiliser le Pages Router ?
Oui, et le modèle est le même : getServerSideProps et pages/api sont le serveur, le reste est client. Ne renvoie depuis getServerSideProps que ce dont la page a besoin, parce que tout ce que tu renvoies est sérialisé dans le HTML.