Aller au contenu principal
Tous les articles
·4 min de lecture

Déployer un SaaS Next.js en production sur Vercel : checklist complète

De la configuration des variables d'environnement au monitoring post-deploy — tout ce qu'il faut vérifier avant de mettre votre SaaS en production.

verceldeploymentproductiondevops

Le déploiement n'est pas un git push

Techniquement, Vercel déploie votre app à chaque push sur main. En pratique, il y a 20 choses à vérifier avant que votre SaaS soit réellement prêt pour la production.

Voici la checklist complète qu'on utilise pour HeartCo.

1. Variables d'environnement

# Base de données
DATABASE_URL="postgresql://..."
DIRECT_URL="postgresql://..."  # Pour les migrations Prisma
 
# Auth
NEXTAUTH_URL="https://votre-app.fr"
NEXTAUTH_SECRET="openssl rand -base64 32"
 
# Stripe
STRIPE_SECRET_KEY="sk_live_..."
STRIPE_WEBHOOK_SECRET="whsec_..."
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="pk_live_..."
 
# Email
RESEND_API_KEY="re_..."
 
# IA
MISTRAL_API_KEY="..."

Règle d'or

Jamais de secrets dans le code ou le .env commité. Utilisez les Environment Variables de Vercel, avec des valeurs différentes pour Preview et Production.

2. Configuration Vercel

// vercel.json
{
  "framework": "nextjs",
  "buildCommand": "pnpm turbo build --filter=web",
  "installCommand": "pnpm install",
  "outputDirectory": "apps/web/.next"
}

Monorepo : Root Directory

Si votre app est dans apps/web/, configurez le Root Directory dans les settings Vercel, ou utilisez outputDirectory dans vercel.json.

3. Base de données

Migrations

# En local, créer la migration
npx prisma migrate dev --name init
 
# En production, appliquer
npx prisma migrate deploy

Important : migrate dev est pour le développement (crée + applique). migrate deploy est pour la production (applique seulement).

Connection pooling

Supabase fournit deux URLs :

  • Transaction mode (?pgbouncer=true) → pour DATABASE_URL (queries normales)
  • Session mode → pour DIRECT_URL (migrations)
datasource db {
  provider  = "postgresql"
  url       = env("DATABASE_URL")
  directUrl = env("DIRECT_URL")
}

4. Domaine et SSL

  1. Ajoutez votre domaine dans Vercel → Settings → Domains
  2. Configurez les DNS chez votre registrar (CNAME vers cname.vercel-dns.com)
  3. Vercel provisionne automatiquement le certificat SSL (Let's Encrypt)
  4. Ajoutez la redirection wwwapex (ou l'inverse)

5. Headers de sécurité

// next.config.ts
const securityHeaders = [
  { key: "X-Frame-Options", value: "DENY" },
  { key: "X-Content-Type-Options", value: "nosniff" },
  { key: "Referrer-Policy", value: "origin-when-cross-origin" },
  { key: "X-XSS-Protection", value: "1; mode=block" },
  {
    key: "Strict-Transport-Security",
    value: "max-age=63072000; includeSubDomains; preload",
  },
];

6. Crons et tâches planifiées

// vercel.json
{
  "crons": [
    {
      "path": "/api/cron/reset-usage",
      "schedule": "0 0 1 * *"
    },
    {
      "path": "/api/cron/trial-expiry",
      "schedule": "0 8 * * *"
    }
  ]
}

N'oubliez pas de protéger vos endpoints cron avec un header secret :

export async function GET(req: Request) {
  const authHeader = req.headers.get("authorization");
  if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
    return new Response("Unauthorized", { status: 401 });
  }
  // ... logique cron
}

7. Monitoring et alertes

Vercel Analytics

// app/layout.tsx
import { Analytics } from "@vercel/analytics/react";
import { SpeedInsights } from "@vercel/speed-insights/next";
 
export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <Analytics />
        <SpeedInsights />
      </body>
    </html>
  );
}

Logs structurés

// Toujours logger le contexte
console.error("[WEBHOOK_STRIPE]", {
  eventType: event.type,
  organizationId: orgId,
  error: error.message,
});

Checklist finale avant le go-live

Infrastructure

  • Variables d'environnement configurées (Production ≠ Preview)
  • Base de données migrée (prisma migrate deploy)
  • Domaine configuré + SSL actif
  • Crons configurés et protégés

Sécurité

  • Headers de sécurité en place
  • NEXTAUTH_SECRET généré (pas la valeur par défaut)
  • Webhook Stripe vérifié par signature
  • Pas de secrets dans le code source

Performance

  • Images optimisées (next/image)
  • Fonts optimisées (next/font)
  • Bundle analysé (@next/bundle-analyzer)

Fonctionnel

  • Inscription → connexion → dashboard fonctionne
  • Paiement Stripe fonctionne (mode live)
  • Emails transactionnels arrivent
  • Dark mode fonctionnel

Monitoring

  • Vercel Analytics activé
  • Error tracking configuré (Sentry recommandé)
  • Uptime monitoring (Vercel / Better Uptime)

Un déploiement réussi, ce n'est pas quand l'app tourne — c'est quand elle tourne, paie, envoie des emails, et vous prévient quand quelque chose casse.