Skip to content
Learni
Voir tous les tutoriels
Sécurité & Données

Comment implémenter une politique de confidentialité RGPD en 2026

Read in English

Introduction

En 2026, la conformité RGPD reste un pilier incontournable pour tout site web traitant des données personnelles. Une politique de confidentialité mal implémentée expose à des amendes jusqu'à 4% du CA mondial. Ce tutoriel avancé vous guide pour créer une solution dynamique dans Next.js 15+ : une page dédiée générée via MDX, un bannière de consentement cookies persistante, une gestion sécurisée des préférences via localStorage chiffré, et une intégration analytics (comme Plausible) conditionnelle au consentement. Contrairement aux générateurs statiques, cette approche est scalable, SEO-friendly et audit-ready. Imaginez votre app comme un coffre-fort : la policy en est la notice d'utilisation, le consent banner le verrou, et les utils le mécanisme interne. À la fin, votre site sera prêt pour les audits CNIL. (128 mots)

Prérequis

  • Node.js 20+ et npm/yarn/pnpm
  • Connaissances avancées en Next.js App Router et TypeScript
  • Familiarité avec RGPD (art. 13/14), cookies (ePrivacy Directive)
  • Outils : Vercel pour déploiement, Plausible.io pour analytics privacy-first

Initialiser le projet Next.js

terminal
npx create-next-app@15 privacy-rgpd-app --typescript --tailwind --eslint --app --src-dir --import-alias "@/*"
cd privacy-rgpd-app
npm install mdx-bundler crypto-js lucide-react
npm install -D @types/crypto-js

Ce script crée un projet Next.js 15 avec TypeScript, Tailwind pour le styling rapide, et MDX pour rendre la policy dynamique. crypto-js chiffre les préférences cookies, lucide-react pour les icônes du banner. Évitez les setups manuels pour gagner du temps ; testez avec npm run dev.

Structure du projet et routing

Créez le dossier src/app/privacy pour la page dédiée, src/components pour le ConsentBanner, et src/lib pour les utils cookies. Ajoutez un middleware pour forcer HTTPS en prod. Le routing App Router gère le SSR de la policy, optimisant le SEO avec meta tags RGPD-specific (ex: robots: noai).

Créer la page Politique de Confidentialité (MDX)

src/app/privacy/page.tsx
import { Metadata } from 'next';
import PrivacyContent from '@/components/PrivacyContent';

export const metadata: Metadata = {
  title: 'Politique de Confidentialité - RGPD 2026',
  description: 'Politique complète des données personnelles conforme RGPD (UE 2016/679).',
  robots: { index: true, follow: true, googleBot: { index: true } },
};

export default function PrivacyPage() {
  return (
    <main className="min-h-screen py-20 px-4 max-w-4xl mx-auto">
      <h1 className="text-4xl font-bold mb-8">Politique de Confidentialité</h1>
      <PrivacyContent />
    </main>
  );
}

Cette page SSR charge le contenu MDX via un composant dédié, avec metadata SEO optimisée pour crawlers. Elle inclut un wrapper responsive. Piège : oubliez pas les meta RGPD pour éviter les blocages search engines ; liez-la dans le footer global.

Contenu MDX de la Politique (template complet)

src/components/PrivacyContent.tsx
import { MDXRemote } from 'next-mdx-remote/rsc';

const mdxSource = `
# Article 1: Informations générales

**Éditeur :** Learni Dev SARL, 1 Rue Exemple, 75001 Paris.
**DPO :** dpo@learnidev.com.

Nous collectons : nom, email, IP (anonymisée).

# Article 2: Bases légales (Art. 6 RGPD)
- Consentement explicite pour cookies marketing.
- Intérêt légitime pour analytics essentiels.

# Article 3: Cookies
| Type | Durée | Finalité |
|------|-------|----------|
| Essentiel | 1 an | Session |
| Analytics | 13 mois | Stats |

# Article 4: Droits (Art. 15-22)
Accès, rectification via [formulaire](mailto:dpo@learnidev.com).

# Article 5: Destinataires
Hébergeur Vercel Inc. (USA, clauses contractuelles types).

Dernière MAJ: 01/01/2026.`;

export default function PrivacyContent() {
  return <MDXRemote source={mdxSource} />;
}

Ce composant rend un template MDX complet et conforme RGPD, avec tableaux pour cookies et liens actionnables. Personnalisez les clauses (ex: remplacez éditeur). Avantage MDX : édition facile sans rebuild ; piège : validez HTML output pour accessibilité WCAG.

Implémentation du gestionnaire de consentement

Le cœur technique : un hook custom useConsent gère les catégories cookies (essential, analytics, marketing). Stockage chiffré en localStorage pour éviter fingerprinting. Le banner s'affiche jusqu'au consentement explicite, conforme ePrivacy.

Hook useConsent avec chiffrement

src/lib/useConsent.ts
import { useState, useEffect } from 'react';
import CryptoJS from 'crypto-js';

const SECRET_KEY = process.env.NEXT_PUBLIC_CONSENT_KEY || 'your-secret-key-change-it';

export type Consent = {
  essential: boolean;
  analytics: boolean;
  marketing: boolean;
};

export function useConsent(): [Consent, (c: Consent) => void, boolean] {
  const [consent, setConsent] = useState<Consent>({ essential: true, analytics: false, marketing: false });
  const [loaded, setLoaded] = useState(false);

  useEffect(() => {
    try {
      const stored = localStorage.getItem('consent');
      if (stored) {
        const bytes = CryptoJS.AES.decrypt(stored, SECRET_KEY);
        const original = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
        setConsent(original);
      }
    } catch {
      // Invalid data: reset
    } finally {
      setLoaded(true);
    }
  }, []);

  const saveConsent = (newConsent: Consent) => {
    const encrypted = CryptoJS.AES.encrypt(JSON.stringify(newConsent), SECRET_KEY).toString();
    localStorage.setItem('consent', encrypted);
    setConsent(newConsent);
  };

  return [consent, saveConsent, loaded];
}

Ce hook décrypte/re-encode les prefs en AES pour privacy renforcée, avec fallback sur defaults. essential always true (nécessaire). Piège majeur : rotatez SECRET_KEY en prod via env ; testez invalid data pour éviter crashes.

Composant Bannière Consentement Cookies

src/components/CookieConsentBanner.tsx
import { useConsent } from '@/lib/useConsent';
import { Check, X, Shield } from 'lucide-react';

export default function CookieConsentBanner() {
  const [consent, saveConsent] = useConsent()[0];
  const loaded = useConsent()[2];

  if (loaded && consent.analytics && consent.marketing) return null;

  const handleAcceptAll = () => saveConsent({ essential: true, analytics: true, marketing: true });
  const handleReject = () => saveConsent({ essential: true, analytics: false, marketing: false });

  return (
    <div className="fixed bottom-4 left-4 right-4 bg-gradient-to-r from-blue-600 to-indigo-700 text-white p-6 rounded-xl shadow-2xl z-50 md:max-w-2xl mx-auto">
      <div className="flex flex-col md:flex-row gap-4 items-center justify-between">
        <div className="flex items-center gap-2">
          <Shield className="w-6 h-6" />
          <div>
            <h3 className="font-bold text-lg">Gestion des cookies RGPD</h3>
            <p className="text-sm opacity-90">Nous utilisons des cookies essentiels. Acceptez pour analytics et marketing.</p>
          </div>
        </div>
        <div className="flex gap-2">
          <button
            onClick={handleAcceptAll}
            className="px-4 py-2 bg-white text-blue-600 rounded-lg font-medium hover:bg-gray-100 transition"
          >
            Tout accepter
          </button>
          <button
            onClick={handleReject}
            className="px-4 py-2 border border-white rounded-lg font-medium hover:bg-white/10 transition"
          >
            Refuser non-essentiels
          </button>
        </div>
      </div>
    </div>
  );
}

Banner responsive Tailwind, avec boutons granulaires (pas de 'tout rejeter' trompeur). Se cache post-consent. Analogie : comme un formulaire opt-in CNIL. Piège : position fixed z-50 pour overlay tout contenu ; granularité évite litiges.

Intégration dans le layout et analytics

Dans src/app/layout.tsx, wrappez en client component. Pour analytics : conditionnez Plausible script sur consent.analytics. Ajoutez lien policy dans footer.

Layout root avec Consent Banner

src/app/layout.tsx
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import './globals.css';
import CookieConsentBanner from '@/components/CookieConsentBanner';

const inter = Inter({ subsets: ['latin'] });

export const metadata: Metadata = {
  title: 'Mon App RGPD Compliant',
  description: 'Site conforme RGPD 2026',
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="fr">
      <body className={inter.className}>{children}</body>
      <CookieConsentBanner />
    </html>
  );
}

Layout global injecte le banner partout. Simple et non-intrusif. Piège : 'use client' si besoin, mais ici SSR ok. Ajoutez

avec lien /privacy pour traçabilité.

Script Analytics conditionnel (Plausible)

src/components/Analytics.tsx
<script dangerouslySetInnerHTML={{
  __html: `
    window.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) };
  `,
}} />
{consent.analytics && (
  <script
    defer
    data-domain="example.com"
    src="https://plausible.io/js/script.outbound-links.js"
    data-api="https://plausible.io/api/event"
  />
)}

Snippet conditionnel : load seulement si consent analytics. Plausible est privacy-first (no cookies). Intégrez dans layout. Piège : defer évite blocage LCP ; trackez outbound sans consent extra.

Configuration Next.js pour headers sécurité

next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  headers: async () => [
    {
      source: '/(.*)',
      headers: [
        {
          key: 'Permissions-Policy',
          value: 'camera=(), microphone=(), geolocation=()',
        },
        {
          key: 'Referrer-Policy',
          value: 'strict-origin-when-cross-origin',
        },
        {
          key: 'Content-Security-Policy',
          value: "default-src 'self'; script-src 'self' plausible.io; frame-ancestors 'none'",
        },
      ],
    },
  ],
  output: 'standalone',
};

module.exports = nextConfig;

Headers renforcent privacy : CSP bloque trackers indésirables, Permissions-Policy stop features invasives. Conforme RGPD data minimization. Piège : testez CSP en report-only d'abord pour éviter breaks.

Bonnes pratiques

  • Granularité consent : 3 catégories mini (essential/analytics/marketing), refresh tous 6 mois.
  • Chiffrement + TTL : Ajoutez expiration aux prefs (ex: 1 an max).
  • Audit logs : Loggez consents anonymisés en backend pour preuves CNIL.
  • i18n ready : Traduisez policy via next-intl.
  • A/B testez banner : Mesurez taux accept via analytics post-consent.

Erreurs courantes à éviter

  • Consent implicite : Jamais de cookies par défaut sauf essential ; amendes CNIL récurrentes.
  • localStorage non-chiffré : Expose à XSS ; utilisez toujours AES ou IndexedDB.
  • Oubli middleware HTTPS : Ajoutez headers: [{ key: 'Strict-Transport-Security', value: 'max-age=31536000' }].
  • Policy statique obsolète : Automatisez MAJ via GitHub Actions pour dates légales.

Pour aller plus loin