Cybersécurité

Caddy headers sécurité CSP HSTS : guide pratique 2026

11 دقائق للقراءة

Les headers de sécurité HTTP sont l’une des défenses les plus simples et efficaces contre une grande variété d’attaques web : XSS, clickjacking, MITM, fuite d’info via Referer. Caddy permet de les configurer en quelques lignes de Caddyfile. Voici le guide pratique 2026 avec config recommandée.

Voir notre guide Caddy complet.

Headers indispensables

exemple.sn {
    reverse_proxy 127.0.0.1:3000

    header {
        # HSTS : forcer HTTPS pendant 1 an, inclure subdomains
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

        # Empêcher MIME sniffing
        X-Content-Type-Options "nosniff"

        # Empêcher clickjacking
        X-Frame-Options "DENY"

        # Politique Referer raisonnable
        Referrer-Policy "strict-origin-when-cross-origin"

        # Désactiver fonctions sensibles non utilisées
        Permissions-Policy "geolocation=(), camera=(), microphone=(), payment=()"

        # Cross-Origin policies
        Cross-Origin-Opener-Policy "same-origin"
        Cross-Origin-Embedder-Policy "require-corp"

        # Retirer le header serveur (réduction surface d'attaque)
        -Server
    }
}

Content Security Policy (CSP)

Le header le plus puissant contre XSS, mais aussi le plus difficile à configurer correctement. Une CSP stricte refuse tout sauf ce qui est explicitement autorisé :

header {
    Content-Security-Policy "
        default-src 'self';
        script-src 'self' 'sha256-XXXX' https://www.googletagmanager.com;
        style-src 'self' 'unsafe-inline';
        img-src 'self' data: https:;
        font-src 'self' data: https://fonts.gstatic.com;
        connect-src 'self' https://api.exemple.sn;
        frame-src 'none';
        object-src 'none';
        base-uri 'self';
        form-action 'self';
        upgrade-insecure-requests;
    "
}

Démarche pragmatique :

  1. Démarrer en mode Content-Security-Policy-Report-Only pendant 1-2 semaines pour collecter les violations sans bloquer
  2. Ajuster la policy au fur et à mesure
  3. Passer en mode enforcement quand stable

HSTS preload

Le flag preload dans HSTS permet de soumettre votre domaine au HSTS preload list, intégré nativement dans Chrome/Firefox/Safari. Une fois préchargé, votre domaine est forcé en HTTPS dès la première visite sans même avoir reçu le header HSTS.

  • Conditions : HSTS max-age ≥ 31536000, includeSubDomains, preload, redirection HTTP→HTTPS
  • Soumission : hstspreload.org
  • Attention : irréversible, vérifiez bien que TOUS vos subdomains supportent HTTPS avant de soumettre

Cookie sécurisé

Caddy ne définit pas les cookies (votre app le fait), mais vérifiez que votre app utilise les flags Secure, HttpOnly, SameSite=Lax/Strict sur les cookies de session. Vous pouvez forcer Set-Cookie via Caddy si nécessaire.

Tester votre config

Adaptation Afrique de l’Ouest

Pour les sites e-commerce ou bancaires en Afrique de l’Ouest, les headers sécurité ne sont plus optionnels : la conformité PCI-DSS, RGPD, et les exigences sectorielles BCEAO l’imposent. La bonne nouvelle : avec Caddy, vous obtenez une note A+ sur securityheaders.com en 10 lignes de configuration.

Erreurs fréquentes

ErreurCauseSolution
CSP casse le siteTrop strict d’embléeDémarrer en Report-Only puis ajuster
HSTS bloque pour toujoursTest HTTPS pas validéDémarrer max-age=300 (5min), puis monter
Frame-Options bloque iframe légitimeDENY trop strictSAMEORIGIN ou ALLOW-FROM
Cookies non secureApp pas configuréeSet-Cookie … Secure; HttpOnly

Pour explorer plus loin

Vous cherchez un hébergement web sérieux et abordable ?

Hostinger offre des serveurs avec installation WordPress en un clic et un panel simple. Notre équipe l’utilise au quotidien.

Découvrir Hostinger →

Lien d affiliation. Si vous achetez via ce lien, le blog reçoit une petite commission sans surcoût pour vous.

Étape 1 — Pourquoi Caddy plutôt qu’Nginx pour les headers

Caddy v2 charge sa configuration via un Caddyfile lisible, recharge à chaud sans interruption (caddy reload) et applique les bonnes pratiques TLS par défaut : HTTP/2, HTTP/3 (QUIC) et OCSP stapling sont actifs sans intervention. Pour une PME à Dakar ou Bamako qui veut durcir un site WordPress ou une API Node sans embaucher un sysadmin, le ratio simplicité/sécurité est imbattable.

Nginx reste excellent, mais Caddy économise 80 % du code de configuration sur les headers de sécurité, sujet de cet article. Comptez 30 lignes de Caddyfile contre 200 lignes de bloc server Nginx pour le même résultat.

Étape 2 — Squelette minimal d’un Caddyfile sécurisé

Créez le fichier /etc/caddy/Caddyfile avec ce squelette. Caddy émet automatiquement un certificat Let’s Encrypt pour le domaine déclaré.

exemple.sn, www.exemple.sn {
    encode zstd gzip
    root * /var/www/exemple
    file_server
    
    header {
        # On posera ici les headers de sécurité dans les étapes suivantes
    }
}

Validez la syntaxe avant tout reload avec caddy validate --config /etc/caddy/Caddyfile. La sortie attendue est Valid configuration. Toute autre réponse signale une erreur de syntaxe à corriger avant de pousser en production.

Étape 3 — HSTS : forcer HTTPS pendant 2 ans

HTTP Strict Transport Security indique au navigateur de ne plus jamais accepter HTTP pour ce domaine. Ajoutez dans le bloc header :

Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"

Le max-age de 63 072 000 secondes correspond à 2 ans, valeur exigée pour soumettre votre domaine sur la HSTS Preload List de Chromium. Activez includeSubDomains uniquement si tous vos sous-domaines (api, mail, blog) sont effectivement en HTTPS. Sinon vous bloquerez l’accès à un sous-domaine non préparé.

Après caddy reload, vérifiez via curl -I https://exemple.sn. Le header doit apparaître. Si vous voulez tester sans risque pendant 1 semaine, mettez d’abord max-age=300 (5 minutes) avant de passer à 2 ans.

Étape 4 — CSP : verrouiller les sources de scripts et styles

Content Security Policy bloque l’exécution de scripts inline ou de tiers non autorisés. C’est le header le plus efficace contre les attaques XSS. Le piège : une CSP trop stricte casse votre site, une CSP trop permissive ne protège rien. Méthode en 3 temps : observer, restreindre, durcir.

Content-Security-Policy-Report-Only "default-src 'self'; \
  script-src 'self' https://www.googletagmanager.com; \
  style-src 'self' 'unsafe-inline'; \
  img-src 'self' data: https:; \
  font-src 'self' data:; \
  connect-src 'self' https://api.exemple.sn; \
  frame-ancestors 'none'; \
  base-uri 'self'; \
  form-action 'self'; \
  report-uri https://exemple.report-uri.com/r/d/csp/reportOnly"

Le mode Report-Only n’applique pas la politique mais envoie les violations à votre endpoint. Laissez tourner 7 jours, dépouillez les rapports, ajustez la liste blanche, puis basculez vers le header Content-Security-Policy sans -Report-Only en production.

Étape 5 — X-Frame-Options et frame-ancestors

Pour empêcher le clickjacking via iframe, deux mécanismes coexistent. X-Frame-Options: DENY est l’ancienne école, supportée partout. frame-ancestors 'none' dans la CSP est la nouvelle école, plus expressive. Posez les deux pour la couverture maximale :

X-Frame-Options "DENY"
# Et dans la CSP : frame-ancestors 'none';

Si votre site doit être encadré par un partenaire (ex. https://partenaire.exemple.sn), remplacez 'none' par l’URL du partenaire. Aucun astérisque jamais : autoriser tout le monde équivaut à désactiver la protection.

Étape 6 — X-Content-Type-Options et Referrer-Policy

Deux headers d’une ligne, à activer sans hésiter sur tous les sites :

X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"

nosniff empêche le navigateur de deviner le type MIME (cause classique d’exécution de scripts déguisés en images). strict-origin-when-cross-origin envoie l’URL complète aux mêmes origines, mais seulement le domaine aux origines tierces : équilibre raisonnable entre analytics et confidentialité.

Étape 7 — Permissions-Policy pour désactiver les API sensibles

Permissions-Policy (anciennement Feature-Policy) coupe l’accès JavaScript aux API caméra, micro, géolocalisation, paiement, USB, etc. Si votre site n’utilise aucune de ces API, désactivez tout :

Permissions-Policy "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=(), interest-cohort=()"

Le dernier interest-cohort=() désactive le défunt FLoC de Chrome, mais reste utile face à ses successeurs publicitaires. Si vous avez besoin de la géolocalisation pour une carte de livraison Cotonou-Bamako, remplacez geolocation=() par geolocation=(self).

Étape 8 — Cross-Origin Isolation (COOP, COEP, CORP)

Trois headers pour isoler votre origine et protéger contre Spectre et compagnie :

Cross-Origin-Opener-Policy "same-origin"
Cross-Origin-Embedder-Policy "require-corp"
Cross-Origin-Resource-Policy "same-origin"

Attention : require-corp peut casser les images ou polices de tiers qui n’envoient pas Cross-Origin-Resource-Policy: cross-origin. Activez-le seulement si vous avez besoin de SharedArrayBuffer (calcul WebAssembly intensif). Sinon, COEP: unsafe-none est plus indulgent.

Étape 9 — Bloc complet et reload sécurisé

Voici le bloc header consolidé que vous pouvez coller. Adaptez les domaines à votre cas.

header {
    Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
    Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'"
    X-Frame-Options "DENY"
    X-Content-Type-Options "nosniff"
    Referrer-Policy "strict-origin-when-cross-origin"
    Permissions-Policy "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()"
    Cross-Origin-Opener-Policy "same-origin"
    Cross-Origin-Resource-Policy "same-origin"
    -Server
}

La directive -Server retire le header qui révèle « Caddy » : moins d’information donnée aux scanners automatisés. Rechargez avec systemctl reload caddy. Si la commande renvoie le prompt sans erreur, la nouvelle config est active. Vérifiez immédiatement avec un outil tiers pour valider la note finale.

Étape 10 — Tester et noter la configuration

Trois outils gratuits à enchaîner après chaque modification : Mozilla Observatory (note A+ visée), Security Headers de Scott Helme (note A+ visée), et SSL Labs côté TLS (note A+ également). Le tour complet prend 5 minutes. Si Mozilla Observatory bloque à 90/100, c’est presque toujours qu’il manque 'unsafe-inline' à votre CSP qui pénalise — élément normal sur WordPress, soustrayez le malus mentalement.

Pour creuser ce sujet, voyez aussi notre guide sur Cloudflare gratuit pour PME africaines qui complète Caddy avec un WAF et un anti-DDoS, et notre tutoriel sur Supabase sur VPS Hetzner avec Coolify qui montre comment placer Caddy devant une stack Supabase.

Étape 11 — Cas particulier WordPress derrière Caddy

WordPress charge des scripts inline (admin-bar, gutenberg) et des styles inline (themes, plugins). Une CSP stricte les bloque. Deux approches : générer des nonces côté plugin (complexe), ou autoriser 'unsafe-inline' sur style-src et script-src en l’acceptant comme compromis. Pour la majorité des PME à Dakar ou Abidjan, le second choix est pragmatique :

Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://*.googletagmanager.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data: https:; connect-src 'self' https:; frame-ancestors 'none'"

Vous perdez 10 points sur Mozilla Observatory mais votre admin WordPress reste fonctionnel. Documentez le compromis dans votre runbook BookStack.

Étape 12 — Reverse proxy vers une API Node ou Supabase

Quand Caddy sert de reverse proxy devant Node, il faut pousser les headers en plus de ceux que l’application renvoie. Utilisez la directive reverse_proxy avec header_up et header_down :

api.exemple.sn {
    reverse_proxy localhost:3000 {
        header_up X-Forwarded-Proto {scheme}
        header_up X-Real-IP {remote}
    }
    header {
        Strict-Transport-Security "max-age=63072000; includeSubDomains"
        X-Content-Type-Options "nosniff"
        Referrer-Policy "no-referrer"
    }
}

Les headers définis dans le bloc header écrasent ceux que renverrait Node si vous utilisez la directive defer. Sans defer, ce sont les headers Node qui priment. Choisissez votre stratégie selon la confiance accordée à votre couche applicative.

Étape 13 — Surveillance continue avec un cron simple

Les configurations dérivent : un développeur ajoute un script tiers, un plugin WordPress modifie les headers, une mise à jour Caddy change un défaut. Programmez un cron quotidien qui interroge l’API Mozilla Observatory et envoie une alerte si la note tombe sous A.

#!/usr/bin/env bash
score=$(curl -s "https://http-observatory.security.mozilla.org/api/v1/analyze?host=exemple.sn&rescan=true" | jq -r '.score')
if [ "$score" -lt 90 ]; then
  curl -X POST -H "Content-Type: application/json" \
    -d "{\"text\":\"Alerte CSP exemple.sn : score $score\"}" \
    "$WEBHOOK_URL"
fi

Planifiez ce script à 03:00 UTC tous les jours. À Dakar comme à Cotonou, vous serez réveillé par un message Discord ou Slack avant que vos clients ne remarquent quoi que ce soit. Investissement : 15 minutes. Retour : tranquillité d’esprit pour des années.

Étape 14 — Versionner le Caddyfile dans un dépôt Git interne

Le Caddyfile est une infrastructure-as-code. Versionnez-le dans un dépôt Git interne (Gitea ou Forgejo auto-hébergé suffit). Chaque modification passe par une pull request relue par un pair, puis un déploiement déclenché par webhook. Vous obtenez l’historique complet des décisions, indispensable pour comprendre dans 6 mois pourquoi telle directive a été ajoutée.

Structure conseillée du dépôt : un fichier par site dans conf.d/, un fichier Caddyfile racine qui les inclut tous via import conf.d/*. Vous gagnez en lisibilité et limitez les conflits Git quand plusieurs développeurs modifient en parallèle.

Étape 15 — Trois erreurs à ne jamais commettre

Première erreur : poser HSTS preload sans tester d’abord en max-age court. Une fois le domaine sur la preload list, retour arrière difficile. Deuxième erreur : oublier que les headers sont insensibles à la casse mais que les valeurs ne le sont pas. nosniff est correct, NoSniff ne l’est pas. Troisième erreur : copier-coller une CSP trouvée sur un blog sans comprendre. Chaque site a ses propres scripts à autoriser.

Un Caddy bien configuré, c’est une PME ouest-africaine qui dort tranquille la nuit, à Dakar, Abidjan, Bamako ou Cotonou.

Étape 16 — Aller plus loin avec les modules Caddy

Caddy expose un écosystème de modules (cache, rate limit, fail2ban-like) compilables via xcaddy. Pour un blog WordPress sur VPS Hetzner CX22 à ~2 960 FCFA/mois, le module caddy-ratelimit permet de bloquer les bots qui martèlent wp-login.php sans installer fail2ban séparément. Ajoutez 5 lignes au Caddyfile et redéployez : la charge CPU chute immédiatement de 30 à 50 % sur les sites visés par le credential stuffing.

Compilez votre binaire personnalisé avec xcaddy build --with github.com/mholt/caddy-ratelimit, déposez le binaire dans /usr/bin/caddy, redémarrez le service. Documentez la version Caddy et la liste des modules dans votre wiki BookStack pour reproduire l’environnement sur un nouveau serveur sans erreur.

مشاركة