TL;DR — Résumé Rapide

Guide complet de Caddy comme proxy inverse avec HTTPS automatique. Caddyfile, politiques d'équilibrage, health checks et Caddyfile de production multi-sites.

Caddy est un serveur web moderne open source écrit en Go dont la caractéristique principale est le HTTPS automatique : il provisionne et renouvelle les certificats TLS depuis Let’s Encrypt ou ZeroSSL dès que vous pointez un domaine vers lui, sans aucune configuration manuelle de Certbot. En tant que proxy inverse, Caddy combine cette gestion TLS sans configuration avec une syntaxe déclarative épurée, un équilibrage de charge intégré, HTTP/2 et HTTP/3 activés par défaut, et un unique binaire compilé statiquement sans dépendances externes. Ce guide couvre tout ce dont vous avez besoin pour exécuter Caddy comme proxy inverse en production : installation, syntaxe du Caddyfile, toutes les politiques d’équilibrage de charge, health checks actifs et passifs, manipulation d’en-têtes, authentification, TLS à la demande, l’API d’administration et un Caddyfile complet pour l’hébergement multi-sites.

Pourquoi Choisir Caddy comme Proxy Inverse

Voici une comparaison directe des principales options de proxy inverse disponibles en 2026 :

FonctionnalitéCaddyNginxTraefikHAProxyApache
HTTPS automatiqueClient ACME intégréManuel (Certbot)Intégré (via ACME)ManuelManuel (mod_md)
Format de configurationCaddyfile (minimal)nginx.conf (verbeux)YAML/TOML/étiquettes Dockerhaproxy.cfghttpd.conf
HTTP/2Par défautConfiguration explicitePar défautNon (TCP uniquement)Configuration explicite
HTTP/3 (QUIC)Par défautExpérimentalVia pluginNonNon
Binaire uniqueOui (Go, sans deps)Non (C, avec modules)Oui (Go)Oui (C)Non
API de configurationAPI REST complèteNonAPI REST complèteSocket de statistiques uniquementNon
Équilibrage de charge8 politiques intégréesLimité intégréMultiples fournisseursExcellentBasique
Empreinte mémoire~20-50 Mo~5-15 Mo~25-60 Mo~5-10 Mo~30-80 Mo
Courbe d’apprentissageFaibleMoyenne-ÉlevéeMoyenneÉlevéeMoyenne-Élevée

Prérequis

  • Un serveur Linux avec Ubuntu 22.04+, Debian 12+ ou une distribution compatible RHEL/CentOS
  • Un nom de domaine avec des enregistrements DNS A/AAAA pointant vers l’IP publique du serveur
  • Les ports 80 et 443 ouverts dans votre pare-feu
  • Accès root ou sudo
  • Aucun autre processus lié aux ports 80 ou 443
sudo ufw allow 80/tcp && sudo ufw allow 443/tcp && sudo ufw status

Installation de Caddy

Ubuntu et Debian (APT)

sudo apt update && sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl

curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
  | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg

curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
  | sudo tee /etc/apt/sources.list.d/caddy-stable.list

sudo apt update && sudo apt install -y caddy
caddy version

RHEL, CentOS, Fedora (DNF)

dnf install 'dnf-command(copr)'
dnf copr enable @caddy/caddy
dnf install caddy

Docker

docker run -d \
  -p 80:80 -p 443:443 -p 443:443/udp \
  -v /chemin/vers/Caddyfile:/etc/caddy/Caddyfile \
  -v caddy_data:/data \
  -v caddy_config:/config \
  caddy:latest

xcaddy — Builds Personnalisées avec Plugins

go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest

xcaddy build \
  --with github.com/caddy-dns/cloudflare \
  --with github.com/mholt/caddy-ratelimit

sudo mv caddy /usr/bin/caddy && sudo systemctl restart caddy

Syntaxe du Caddyfile

Structure

# Bloc d'options globales
{
    email admin@exemple.fr
    admin off
    grace_period 10s
}

# Bloc de site — le domaine déclenche le HTTPS automatique
app.exemple.fr {
    directive argument
}

Matchers

exemple.fr {
    @api path /api/*
    reverse_proxy @api localhost:8000

    reverse_proxy /statique/* localhost:9000

    file_server *
}

Espaces réservés (Placeholders)

exemple.fr {
    reverse_proxy localhost:3000 {
        header_up X-IP-Reel        {remote_host}
        header_up X-Port-Trans     {server_port}
        header_up X-ID-Requete     {uuid}
    }
}

Configuration du Proxy Inverse

Backend Unique de Base

app.exemple.fr {
    reverse_proxy localhost:3000
}

Cette ligne unique fournit : HTTPS automatique, HTTP/2, HTTP/3, redirection HTTP→HTTPS, transfert des en-têtes X-Forwarded-For et support transparent des WebSocket.

Routage par Chemin

exemple.fr {
    reverse_proxy /api/*    localhost:8000
    reverse_proxy /ws/*     localhost:4000
    reverse_proxy /admin/*  localhost:9000

    root * /var/www/frontend
    file_server
}

Manipulation des En-têtes

app.exemple.fr {
    reverse_proxy localhost:3000 {
        header_up Host              {upstream_hostport}
        header_up X-IP-Reel         {remote_host}
        header_up X-Proto-Trans     {scheme}

        header_up -Authorization

        header_down -X-ID-Serveur-Interne
        header_down Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
    }
}

WebSocket et Connexions Longue Durée

ws.exemple.fr {
    reverse_proxy localhost:4000 {
        flush_interval -1
    }
}

Équilibrage de Charge

Plusieurs Backends

app.exemple.fr {
    reverse_proxy localhost:3001 localhost:3002 localhost:3003
}

Toutes les Politiques d’Équilibrage de Charge

app.exemple.fr {
    reverse_proxy localhost:3001 localhost:3002 localhost:3003 {
        lb_policy least_conn         # Backend avec le moins de connexions actives
        # lb_policy round_robin      # Rotation séquentielle (par défaut)
        # lb_policy first            # Toujours le premier backend disponible
        # lb_policy random           # Sélection aléatoire
        # lb_policy ip_hash          # IP client → backend cohérent
        # lb_policy cookie caddy_lb  # Persistance de session par cookie
        # lb_policy header X-Shard   # Routage par valeur d'en-tête
        # lb_policy uri_hash         # URI de la requête → backend cohérent
    }
}

Health Checks Actifs

app.exemple.fr {
    reverse_proxy localhost:3001 localhost:3002 localhost:3003 {
        lb_policy least_conn

        health_uri      /health
        health_interval 15s
        health_timeout  5s
        health_status   200
    }
}

Health Checks Passifs

app.exemple.fr {
    reverse_proxy localhost:3001 localhost:3002 localhost:3003 {
        lb_policy round_robin

        fail_duration     30s
        max_fails         3
        unhealthy_latency 500ms
        unhealthy_status  5xx
    }
}

HTTPS Automatique en Détail

Types de Défis ACME

{
    email admin@exemple.fr
}

# HTTP-01 (par défaut)
exemple.fr {
    reverse_proxy localhost:3000
}

# TLS-ALPN-01 : utilise le port 443
tls-seulement.exemple.fr {
    tls {
        challenges tls-alpn-01
    }
    reverse_proxy localhost:3001
}

# DNS-01 : sans ports entrants — nécessite xcaddy + plugin DNS
wildcard.exemple.fr {
    tls {
        dns cloudflare {env.CF_API_TOKEN}
    }
    reverse_proxy localhost:3002
}

TLS à la Demande pour les Domaines Dynamiques

{
    on_demand_tls {
        ask      http://localhost:9001/verifier-domaine
        interval 2m
        burst    5
    }
}

:443 {
    tls {
        on_demand
    }
    reverse_proxy localhost:3000
}

CA Interne pour le Développement

{
    local_certs
}

localhost, 127.0.0.1, ::1 {
    reverse_proxy localhost:3000
}

Authentification

Authentification Basique HTTP

admin.exemple.fr {
    basicauth /admin/* {
        alice $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GT.VVKf3KCrXxpHPkAXmn9sQHO
    }
    reverse_proxy localhost:9000
}
caddy hash-password --plaintext 'votremotdepasse'

Authentification Déléguée (Authelia, oauth2-proxy)

app.exemple.fr {
    forward_auth authelia:9091 {
        uri /api/verify?rd=https://auth.exemple.fr
        copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
    }
    reverse_proxy localhost:3000
}

Serveur de Fichiers et SPA

Serveur de Fichiers Statiques

statique.exemple.fr {
    root * /var/www/html
    encode gzip zstd
    file_server {
        hide .git .env .htaccess
    }
}

Application à Page Unique (SPA)

spa.exemple.fr {
    root * /var/www/app/dist
    encode gzip zstd
    try_files {path} /index.html
    file_server
}

Compression et Journalisation des Accès

exemple.fr {
    encode {
        zstd
        gzip 6
        minimum_length 1024
    }

    log {
        output file /var/log/caddy/acces.log {
            roll_size    100MiB
            roll_keep    10
            roll_keep_for 720h
        }
        format json
        level  INFO
    }

    reverse_proxy localhost:3000
}

L’API d’Administration de Caddy

# Voir la configuration actuelle
curl http://localhost:2019/config/

# Recharger la configuration depuis le Caddyfile
curl -X POST http://localhost:2019/load \
  -H "Content-Type: text/caddyfile" \
  --data-binary @/etc/caddy/Caddyfile

Caddyfile de Production pour Sites Multiples

{
    email admin@exemple.fr
    grace_period 30s
    admin unix//run/caddy/admin.sock
}

(en_tetes_securite) {
    header {
        Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
        X-Content-Type-Options    "nosniff"
        X-Frame-Options           "SAMEORIGIN"
        Referrer-Policy           "strict-origin-when-cross-origin"
        -Server
        -X-Powered-By
    }
}

app.exemple.fr {
    import en_tetes_securite
    encode zstd gzip

    log {
        output file /var/log/caddy/app-acces.log { roll_size 50MiB; roll_keep 7 }
        format json
    }

    reverse_proxy localhost:3001 localhost:3002 localhost:3003 {
        lb_policy       least_conn
        health_uri      /health
        health_interval 10s
        health_timeout  3s
        fail_duration   30s
        max_fails       3

        header_up X-IP-Reel        {remote_host}
        header_up X-Proto-Trans    {scheme}
        header_down -X-Trace-Interne
    }
}

www.exemple.fr {
    import en_tetes_securite
    root * /var/www/frontend/dist
    encode zstd gzip

    @statique path *.css *.js *.png *.jpg *.webp *.gif *.svg *.woff2 *.ico
    header @statique Cache-Control "public, max-age=31536000, immutable"

    try_files {path} /index.html
    file_server
}

Pièges et Cas Particuliers

Le port 80 doit être accessible pour les défis HTTP-01. Si votre fournisseur cloud bloque le port 80, utilisez le défi tls-alpn-01 ou DNS-01 via xcaddy avec un plugin DNS.

Limites de débit de Let’s Encrypt. Le serveur ACME de production autorise 5 certificats dupliqués par domaine par semaine. Pendant les tests, définissez acme_ca https://acme-staging-v02.api.letsencrypt.org/directory.

L’utilisateur caddy a besoin d’un accès en lecture à vos racines web. Le service systemd s’exécute en tant qu’utilisateur système caddy. Exécutez toujours sudo chown -R caddy:caddy /var/www/votresite.

L’intervalle de vidage est important pour le streaming. Les Server-Sent Events (SSE) et les API de streaming nécessitent flush_interval -1 dans le bloc reverse_proxy.

Dépannage

# Valider la syntaxe du Caddyfile
caddy validate --config /etc/caddy/Caddyfile

# Formater automatiquement le Caddyfile
caddy fmt --overwrite /etc/caddy/Caddyfile

# Surveiller les journaux en direct
sudo journalctl -u caddy -f --no-pager

# Vérifier les ports d'écoute de Caddy
sudo ss -tlnp | grep caddy

# Vérifier le certificat TLS
echo | openssl s_client -connect exemple.fr:443 -servername exemple.fr 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates

Résumé

  • Caddy est un unique binaire Go sans dépendances externes qui fournit HTTPS automatique via ACME, HTTP/2 et HTTP/3 par défaut, et une syntaxe Caddyfile minimale
  • La directive reverse_proxy gère le proxying, l’équilibrage de charge, la manipulation d’en-têtes, le support WebSocket et les health checks dans un seul bloc
  • Huit politiques d’équilibrage de charge couvrent tous les scénarios, du round-robin sans état à l’affinité de session par cookie
  • Les health checks actifs et passifs peuvent être combinés pour une détection robuste des pannes
  • Le TLS à la demande permet l’émission de certificats par tenant pour les plateformes SaaS sans pré-configuration
  • Utilisez des snippets pour partager des blocs de configuration entre plusieurs définitions de sites

Articles Connexes