iducationducation
IndexArticlesFormationsProfilOutilsBibliothech
N°014 — 2026
Navigation
01Index02Articles03Formations04Profil05Outils06Bibliothech
N°014 — 2026
iducationducation
IndexArticlesFormationsProfilOutilsBibliothech
N°014 — 2026
Navigation
01Index02Articles03Formations04Profil05Outils06Bibliothech
N°014 — 2026
Formations
HCL / JSON / Bash · Avancé

Caddy

Caddy de A à Z pour experts : Admin API REST, HTTPS automatique (ACME, DNS challenge, PKI interne), reverse proxy avancé, modules, on-demand TLS, clustering et production.

Caddy 2CaddyfileAdmin APIACMEDocker
01Pourquoi Caddy — ce qu'Apache et Nginx ne font pas02Architecture interne — Apps, modules et config JSON03Caddyfile avancé — matchers, snippets, directives04Admin API — configuration dynamique en JSON05HTTPS et PKI — ACME, wildcards, on-demand TLS, CA interne06Reverse proxy avancé — load balancing, health checks, transforms07Modules et plugins — forward_auth, security, ratelimit, modules custom08Production — logs, métriques Prometheus, clustering, Docker, Kubernetes
Chapitre 6·35 min

Reverse proxy avancé — load balancing, health checks, transforms

Le reverse_proxy de Caddy est l'un des handlers les plus riches. Ce chapitre couvre tout ce qui va au-delà du basique reverse_proxy localhost:3000 : stratégies de load balancing, health checks actifs et passifs, transforms de headers, streaming, retries intelligents.

Load balancing — stratégies

Caddy propose plusieurs politiques de sélection d'upstream :

round_robin         Distribution circulaire (défaut)
least_conn          Upstream avec le moins de connexions actives
random              Sélection aléatoire
random_choose N     N upstreams aléatoires, puis least_conn entre eux
first               Premier upstream disponible (priorité fixe)
ip_hash             Hash de l'IP client → upstream fixe (sticky)
uri_hash            Hash de l'URI → upstream fixe
header              Hash d'un header spécifique
cookie              Sticky par cookie

Least connections — recommandé pour les APIs

api.example.com {
    reverse_proxy backend-1:3000 backend-2:3000 backend-3:3000 {
        lb_policy least_conn
    }
}

Sticky par cookie — sessions sans état partagé

{
  "handler": "reverse_proxy",
  "upstreams": [
    {"dial": "backend-1:3000"},
    {"dial": "backend-2:3000"},
    {"dial": "backend-3:3000"}
  ],
  "load_balancing": {
    "selection_policy": {
      "policy": "cookie",
      "name": "srv",
      "secret": "change-me-to-something-random-32chars"
    }
  }
}

Poids différents

{
  "upstreams": [
    {"dial": "primary:3000", "max_requests": 100},
    {"dial": "secondary:3000", "max_requests": 20}
  ],
  "load_balancing": {
    "selection_policy": {"policy": "first"}
  }
}

Avec first, le trafic va sur primary jusqu'à 100 connexions simultanées, puis déborde sur secondary.

Health checks actifs

Les health checks actifs sondent les upstreams périodiquement, indépendamment du trafic :

{
  "handler": "reverse_proxy",
  "upstreams": [
    {"dial": "backend-1:3000"},
    {"dial": "backend-2:3000"}
  ],
  "health_checks": {
    "active": {
      "uri": "/health",
      "port": 8080,
      "interval": "10s",
      "timeout": "3s",
      "max_size": 1048576,
      "expect_status": 200,
      "expect_body": "ok",
      "headers": {
        "X-Health-Check": ["caddy"]
      }
    }
  }
}
# Vérifier l'état des upstreams en temps réel
curl -s localhost:2019/reverse_proxy/upstreams | jq .
# [
#   {"address": "backend-1:3000", "healthy": true, "num_requests": 42, "fails": 0},
#   {"address": "backend-2:3000", "healthy": false, "num_requests": 0, "fails": 5}
# ]

Health checks passifs

Les health checks passifs observent les réponses réelles et marquent un upstream comme défaillant s'il génère trop d'erreurs :

{
  "health_checks": {
    "passive": {
      "fail_duration": "30s",
      "max_fails": 3,
      "unhealthy_status": [500, 502, 503, 504],
      "unhealthy_latency": "5s",
      "unhealthy_request_count": 50
    }
  }
}

Un upstream marqué défaillant reste exclu pendant fail_duration, puis est retesté.

Retries et circuit breaker

{
  "handler": "reverse_proxy",
  "upstreams": [
    {"dial": "backend-1:3000"},
    {"dial": "backend-2:3000"}
  ],
  "load_balancing": {
    "try_duration": "10s",
    "try_interval": "250ms",
    "retry_match": [
      {
        "status_code": [502, 503, 504]
      }
    ]
  },
  "health_checks": {
    "passive": {
      "fail_duration": "60s",
      "max_fails": 5,
      "unhealthy_status": [502, 503]
    }
  }
}

try_duration : durée max pendant laquelle Caddy essaie les upstreams disponibles. Si tous échouent, retourne l'erreur au client. try_interval : délai entre chaque tentative.

Transforms de headers

Headers entrants (vers l'upstream)

reverse_proxy backend:3000 {
    header_up Host {upstream_hostport}
    header_up X-Real-IP {remote_host}
    header_up X-Forwarded-For {remote_host}
    header_up X-Forwarded-Proto {scheme}
    header_up X-Forwarded-Host {host}
    header_up X-Request-ID {uuid}
 
    # Supprimer un header
    header_up -Authorization
 
    # Réécrire un header
    header_up User-Agent "MyApp/1.0"
}

Headers sortants (vers le client)

reverse_proxy backend:3000 {
    # Supprimer les headers qui révèlent la stack interne
    header_down -X-Powered-By
    header_down -Server
    header_down -X-AspNet-Version
 
    # Ajouter des headers de sécurité
    header_down Strict-Transport-Security "max-age=31536000; includeSubDomains"
    header_down X-Content-Type-Options "nosniff"
    header_down X-Frame-Options "DENY"
}

Réécrire l'URI avant le proxy

# Strip le préfixe /api avant de forwarder
handle /api/* {
    uri strip_prefix /api
    reverse_proxy backend:3000
}
 
# Réécrire vers un chemin différent
handle /old-path/* {
    rewrite * /new-path{path}
    reverse_proxy backend:3000
}

Buffering et streaming

Par défaut, Caddy streame les requêtes et réponses. Pour les gros uploads ou les SSE/WebSockets :

{
  "handler": "reverse_proxy",
  "upstreams": [{"dial": "backend:3000"}],
  "request_buffers": 0,
  "response_buffers": 0,
  "flush_interval": -1
}

flush_interval: -1 désactive le buffering de réponse — indispensable pour Server-Sent Events et les réponses de streaming LLM.

# WebSocket — Caddy gère automatiquement Upgrade
ws.example.com {
    reverse_proxy backend:3000 {
        header_up Host {host}
        header_up Upgrade {header.Upgrade}
        header_up Connection {header.Connection}
    }
}

Transports — TLS vers l'upstream

Si l'upstream nécessite TLS (connexions backend-to-backend encryptées) :

{
  "handler": "reverse_proxy",
  "upstreams": [{"dial": "internal-service:8443"}],
  "transport": {
    "protocol": "http",
    "tls": {
      "root_ca_pool": ["/etc/caddy/internal-ca.crt"],
      "client_certificate_file": "/etc/caddy/client.crt",
      "client_certificate_key_file": "/etc/caddy/client.key",
      "server_name": "internal-service",
      "insecure_skip_verify": false
    },
    "dial_timeout": "5s",
    "response_header_timeout": "30s",
    "keep_alive": {
      "enabled": true,
      "probe_interval": "30s",
      "max_idle_conns": 100,
      "idle_conn_timeout": "90s"
    }
  }
}

Réponse de maintenance par upstream

example.com {
    @up_backend {
        remote_ip private_ranges  # requêtes internes
    }
 
    reverse_proxy backend-1:3000 backend-2:3000 {
        lb_policy least_conn
 
        health_checks {
            active {
                uri /health
                interval 5s
                timeout 2s
            }
            passive {
                fail_duration 30s
                max_fails 3
                unhealthy_status 500 502 503
            }
        }
 
        # Réponse si tous les upstreams sont down
        handle_response {
            @bad_status status 502 503
            handle @bad_status {
                respond "Service temporairement indisponible" 503
            }
        }
    }
}

Pattern Blue/Green avec l'Admin API

Déployement blue/green sans coupure de service, piloté par l'API :

#!/usr/bin/env bash
# deploy-bluegreen.sh
 
CADDY_API="http://localhost:2019"
PROXY_ID="proxy-production"
 
# Déployer la nouvelle version sur le port 3001 (green)
docker run -d --name app-green -p 3001:3000 my-app:new-version
 
# Attendre que l'app soit prête
until curl -sf http://localhost:3001/health; do sleep 1; done
 
# Basculer le trafic vers green
curl -X PATCH "$CADDY_API/id/$PROXY_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "handler": "reverse_proxy",
    "upstreams": [{"dial": "localhost:3001"}]
  }'
 
echo "Traffic switched to green (port 3001)"
 
# Laisser quelques secondes pour que les connexions actives se terminent
sleep 10
 
# Arrêter le bleu
docker stop app-blue && docker rm app-blue
docker rename app-green app-blue

Le reverse proxy avancé est couvert. Prochain chapitre : modules et plugins — forward_auth, caddy-security, caddy-ratelimit, et écrire un module custom avec xcaddy.

Précédent
HTTPS et PKI — ACME, wildcards, on-demand TLS, CA interne
Suivant
Modules et plugins — forward_auth, security, ratelimit, modules custom

Développeur fullstack passionné. J'apprends en construisant et je documente tout — front, back, outils. Le code s'apprend mieux en public.

Naviguer

IndexTous les articlesFormationsProfilOutilsBibliothech

Ailleurs

GitHub RSS

Newsletter

Les articles, libs et découvertes. Une fois par semaine, pas plus.

© 2026 William LoreeConçu & codé à la main