Aller au contenu
ZarexDocs

Next.js

Protégez une application Next.js avec @zarex/next, dans un route handler ou pour toutes les routes.

@zarex/next fonctionne dans les route handlers et dans le fichier qui intercepte toutes les requêtes (proxy.ts à partir de Next.js 16, middleware.ts avant). Il n'utilise que les objets Request et Response standard.

Installation

npm install @zarex/next

Ajoutez votre clé dans .env.local (sans préfixe NEXT_PUBLIC_) :

.env.local
ZAREX_KEY=zarex_test_...

Protéger un route handler

app/api/produits/route.ts
import { withZarex, shield, rateLimit } from "@zarex/next";

export const GET = withZarex(
  {
    key: process.env.ZAREX_KEY!,
    ipSource: "vercel",
    rules: [shield(), rateLimit({ max: 100, window: "1m" })],
  },
  async () => Response.json({ produits: [] })
);

Une requête refusée reçoit automatiquement 403, ou 429 avec Retry-After, sans que votre handler soit appelé.

Protéger toutes les routes

proxy.ts
import { NextResponse } from "next/server";
import { zarexNext, shield, rateLimit } from "@zarex/next";

const guard = zarexNext({
  key: process.env.ZAREX_KEY!,
  ipSource: "vercel",
  rules: [shield(), rateLimit({ max: 100, window: "1m" })],
});

export async function proxy(request: Request) {
  return (await guard(request)) ?? guard.finalize(NextResponse.next(), request);
}

guard(request) renvoie la réponse de refus, ou null si la requête peut continuer.

N'oubliez pas finalize

Si vous activez secureHeaders ou responseGuard, appelez guard.finalize(...) aussi sur la réponse autorisée, comme ci-dessus : sinon ces protections ne s'appliquent jamais au trafic autorisé. Avec withZarex, c'est automatique.

L'adresse IP du visiteur

Next.js ne donne jamais l'adresse de la connexion : Zarex la lit dans un en-tête écrit par votre hébergeur ou par votre proxy.

HébergementipSource
VercelRien à faire (détecté automatiquement), ou "vercel".
Fly.ioRien à faire (détecté automatiquement), ou "fly".
Cloudflare Workers ou PagesRien à faire (détecté automatiquement), ou "cloudflare-workers".
VPS ou Docker derrière nginx ou Caddy{ header: "x-real-ip" }, l'en-tête écrit par votre proxy.
Cloudflare devant votre serveur{ header: "cf-connecting-ip" }

Ailleurs, sans ipSource, l'application refuse de démarrer plutôt que de deviner.

Pas de { proxies } ni de "cloudflare" avec Next.js

Ces réglages ont besoin de l'adresse de la connexion, que Next.js ne fournit pas : les requêtes passeraient sans être vérifiées. Avec { header }, le proxy doit écraser l'en-tête, et l'application ne doit être joignable que par lui. Configuration nginx et Caddy : voir L'adresse IP du visiteur.

En local

next dev ne fournit aucun en-tête d'adresse : sans précaution, les requêtes passent sans être vérifiées (un avertissement l'indique dans les logs). Avec ipSource: { header: "x-real-ip" }, testez en ajoutant l'en-tête vous-même :

curl -H "X-Real-IP: 41.0.0.1" "http://localhost:3000/api/produits?id=1%27%20OR%20%271%27=%271"

Données de formulaire

withZarex lit la requête avant votre handler : pour valider un e-mail, un téléphone ou un texte saisi dans un formulaire, utilisez plutôt createZarex et appelez protect vous-même, avec les données déjà lues.

app/actions/inscription.ts
"use server";
import { headers } from "next/headers";
import { createZarex, emailValidation, rateLimit } from "@zarex/next";

const zarex = createZarex({
  key: process.env.ZAREX_KEY!,
  ipSource: "vercel",
  rules: [rateLimit({ max: 5, window: "10m" }), emailValidation()],
});

export async function inscrire(email: string) {
  const h = await headers();
  const decision = await zarex.protect(
    { method: "POST", path: "/inscription", headers: h },
    { email }
  );
  if (decision.denied) return { ok: false };
  // … créer le compte
  return { ok: true };
}

Personnaliser le refus

withZarex(
  {
    key: process.env.ZAREX_KEY!,
    rules: [shield()],
    onDeny: (request, decision) =>
      Response.json({ erreur: "Requête refusée", id: decision.id }, { status: 403 }),
  },
  handler
);

onDeny reçoit la décision complète et doit toujours renvoyer une Response : c'est elle qui est envoyée au visiteur à la place de votre handler. Si elle n'en renvoie pas (un return oublié, par exemple), le SDK envoie la réponse de refus par défaut et le signale dans vos logs : la requête reste bloquée (depuis la version 0.1.4). onDegraded(request, decision) est appelé quand Zarex n'a pas pu décider et que la requête a été laissée passer.

Pour aller plus loin

Sur cette page