Quasi tutti i buchi di un'app Next.js creata con l'IA vengono da un'unica confusione: non sapere quale parte del codice gira sul server e quale finisce nel browser. Next.js mescola le due nello stesso progetto, e Cursor, v0 o Bolt attraversano il confine senza avvisare. Questa guida spiega quel modello mentale in cinque minuti, ripassa ciò che gli strumenti di IA sbagliano in Next.js, ti lascia un blocco di regole da incollare nel progetto, un prompt di revisione e quattro verifiche dall'esterno. La lista punto per punto di cosa sistemare è nella checklist Next.js prima del lancio; qui c'è il perché.
Parte 1: dove gira ogni cosa
Cinque posti, due mondi
Nell'App Router ci sono cinque posti in cui può vivere il tuo codice: i Server Components (solo server, non arrivano mai al browser), i Client Components (marcati con "use client", spediti interi al browser), i Route Handlers (app/api/.../route.ts, URL pubblici), le Server Actions (funzioni con "use server" che il client invoca via POST) e il middleware (chiamato proxy nelle versioni più recenti), che gira prima di ogni richiesta. Gli ultimi tre più i Server Components sono il mondo del server; i Client Components sono il mondo del browser. Tutto ciò che importi in un Client Component, costanti e funzioni di supporto comprese, viaggia nel bundle.
Cosa arriva al browser
- Le variabili d'ambiente con prefisso
NEXT_PUBLIC_, incorporate al momento della build. Le altre esistono solo sul server. - Qualsiasi dato che un Server Component passa come prop a un Client Component. Se passi l'oggetto
userintero, viaggia con l'hash della password e l'email. - I file della cartella
public/, così come sono. - Le source map, se le attivi con
productionBrowserSourceMaps.
Tre meccanismi che evitano di attraversare il confine
Il pacchetto server-only: un import 'server-only' in cima a un file fa fallire la build se qualcuno lo importa da un Client Component. Usalo in tutto ciò che tocca il database o legge segreti. Un livello di accesso ai dati: un modulo che centralizza il controllo della sessione e restituisce solo i campi necessari (un DTO), invece di interrogare il database da ogni pagina. E la regola che ogni punto d'ingresso controlla la sessione: Route Handlers, Server Actions e Server Components che leggono dati privati, ciascuno per conto suo. Il middleware non sostituisce quel controllo, come ha dimostrato la CVE-2025-29927.
Cosa Next.js non fa per te
Non aggiunge header di sicurezza, non limita le richieste, non valida l'input delle Server Actions e non protegge le rotte solo perché stanno in una cartella chiamata (admin). Tutto questo è codice tuo o configurazione in next.config.
Parte 2: cosa l'IA sbaglia in Next.js
- Tutto con "use client". Quando un componente ha bisogno di stato, l'IA lo marca come client e si trascina dietro la query al database o la chiave dell'API. Dividi: il componente server recupera i dati, quello client disegna soltanto.
- Protezione solo nel middleware. Il middleware reindirizza se non c'è il cookie e l'handler si fida. Controlla la sessione anche nell'handler; è ciò che la CVE del middleware ha insegnato, e le versioni precedenti a 15.2.3 o 14.2.25 restano vulnerabili.
- Route Handlers e Server Actions senza autenticazione né validazione. Una Server Action è un endpoint pubblico anche senza URL visibile. Controlla chi chiama e valida il body con uno schema (zod o simile) prima di toccare qualsiasi cosa.
- Segreti con il prefisso pubblico.
NEXT_PUBLIC_OPENAI_KEYperché il componente client "ne aveva bisogno". La chiave va in un Route Handler e il client chiama l'handler. - remotePatterns con wildcard.
hostname: '**'nella configurazione delle immagini trasforma il tuo ottimizzatore di immagini in un proxy aperto verso qualsiasi URL. - Redirect con parametro.
redirect(searchParams.next)senza verificare che la destinazione sia un percorso interno: phishing con il tuo dominio. - fetch verso URL dell'utente. Un handler che scarica quello che gli passano può raggiungere servizi interni della tua rete o del provider.
- dangerouslySetInnerHTML con contenuto utente. XSS classico; se ti serve HTML, sanificalo sul server.
- Senza header né limiti. Né CSP, né HSTS, né X-Frame-Options, e un endpoint che chiama un modello a pagamento senza tetto per utente.
Un blocco di regole per il tuo progetto
Incollalo nel file di regole che il tuo strumento legge (.cursor/rules, AGENTS.md o l'equivalente) perché si applichi a ogni generazione:
Regole di sicurezza per questo progetto Next.js:
- I componenti sono Server Components a meno che non abbiano bisogno di stato o eventi del browser. Un Client Component non importa mai moduli che accedono al database o leggono process.env senza prefisso.
- Ogni modulo che tocca il database o i segreti inizia con import 'server-only'.
- Non usare mai NEXT_PUBLIC_ per una chiave segreta. Le chiamate a servizi con chiave vanno in un Route Handler o in una Server Action.
- Ogni Route Handler e ogni Server Action controlla la sessione e valida l'input con uno schema prima di fare qualsiasi cosa. Il middleware non conta come autenticazione.
- Passa ai Client Components solo i campi necessari, mai l'oggetto intero dell'utente o della riga.
- I redirect accettano solo percorsi interni che iniziano con una singola barra.
- images.remotePatterns elenca domini precisi, mai un wildcard di host.
- Non usare dangerouslySetInnerHTML con contenuto utente.
- next.config definisce gli header di sicurezza e non attiva productionBrowserSourceMaps.
Un prompt di revisione prima del deploy
Rivedi la sicurezza di questa app Next.js e non cambiare ancora nulla. Rispondi con una lista:
1. Client Components che importano moduli del database, leggono segreti o ricevono come prop oggetti con campi sensibili.
2. Route Handlers e Server Actions senza controllo della sessione o senza validazione dell'input.
3. Rotte la cui unica protezione è il middleware.
4. Variabili NEXT_PUBLIC_ che contengono chiavi segrete.
5. Redirect che accettano una destinazione fornita dall'utente, fetch verso URL dell'utente e usi di dangerouslySetInnerHTML.
6. remotePatterns con wildcard, productionBrowserSourceMaps attivo e header di sicurezza assenti in next.config.
7. Versione di Next.js e se è precedente a 15.2.3 o 14.2.25.
Per ogni punto, proponi la modifica esatta e aspetta la mia conferma.
Verifica il risultato dall'esterno
Quattro comandi contro il tuo dominio. Header e versione del middleware:
curl -sI https://tuodominio.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://tuodominio.com/dashboard
Se il secondo restituisce 200 su una rotta che dovrebbe reindirizzare al login, la tua versione è vulnerabile alla CVE del middleware: aggiorna oggi. Poi, source map e rotte API che il bundle rivela:
curl -s https://tuodominio.com | grep -oE '/_next/static/chunks/[^"]+\.js' | head -3
curl -s https://tuodominio.com/_next/static/chunks/main-app-XXXX.js | grep -oE '"/api/[^"]+"' | sort -u
Richiedi ogni rotta API senza sessione: la risposta corretta è un 401 o 403. Scanaris fa queste verifiche e una quarantina di altre in 30 secondi: rileva che l'app è Next.js e la sua versione, prova l'header della CVE, cerca segreti nei chunk, controlla se le source map sono pubblicate, verifica header, CSP, cookie, CORS e file esposti, e ti dà un fix prompt per ogni errore da incollare in Cursor o v0. Se fai il deploy su Vercel, la guida Vercel copre le anteprime pubbliche e le variabili d'ambiente.
Domande frequenti
Le Server Actions sono sicure di default?
Sono più difficili da scoprire di un Route Handler, ma altrettanto pubbliche: chiunque abbia l'identificatore dell'azione può invocarla. Trattale come un endpoint: sessione e validazione dentro la funzione.
Basta il middleware per proteggere la dashboard?
No. Serve per reindirizzare in fretta, ma il controllo vero va in ogni pagina, handler e azione. La CVE-2025-29927 permetteva di aggirare il middleware con un header; anche su una versione corretta, la regola resta la stessa.
Posso continuare a usare il Pages Router?
Sì, e il modello è lo stesso: getServerSideProps e pages/api sono il server, il resto è client. Restituisci da getServerSideProps solo ciò che serve alla pagina, perché tutto quello che restituisci viene serializzato nell'HTML.