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 5·40 min

HTTPS et PKI — ACME, wildcards, on-demand TLS, CA interne

Le système TLS de Caddy n'est pas un wrapper autour de Certbot. C'est un moteur ACME natif avec gestion de certificats, renouvellement automatique, challenges multiples, PKI interne, et on-demand TLS pour des milliers de domaines. Ce chapitre couvre tout.

Comment Caddy obtient un certificat

Quand Caddy démarre avec example.com dans sa config, il suit ce chemin :

1. Domaine qualifié publiquement ? (pas *.local, pas IP)
2. Certificat déjà en cache et valide > 30 jours ? → utilise-le
3. Challenge ACME sélectionné (HTTP-01 par défaut, TLS-ALPN-01 fallback)
4. Obtention du certificat, stockage dans storage backend
5. OCSP stapling activé automatiquement
6. Renouvellement déclenché quand < 30 jours de validité restants

Tout cela sans configuration supplémentaire. La seule chose requise : que le domaine DNS pointe vers votre serveur et que les ports 80/443 soient accessibles.

Challenges ACME

HTTP-01 (défaut)

Caddy crée un fichier temporaire sur /.well-known/acme-challenge/ et le sert sur le port 80. Let's Encrypt le vérifie.

Limitations : nécessite le port 80 ouvert, ne supporte pas les wildcards.

TLS-ALPN-01

Caddy répond au challenge via une extension TLS spéciale sur le port 443. Utile si le port 80 est bloqué.

{
  "apps": {
    "tls": {
      "automation": {
        "policies": [{
          "subjects": ["example.com"],
          "issuers": [{
            "module": "acme",
            "challenges": {
              "tls_alpn": {}
            }
          }]
        }]
      }
    }
  }
}

DNS-01 (wildcards et domaines privés)

Le seul challenge qui supporte les wildcards (*.example.com) et les domaines non-accessibles depuis l'extérieur. Nécessite un module DNS provider.

xcaddy build \
  --with github.com/caddy-dns/cloudflare \
  --with github.com/caddy-dns/route53 \
  --with github.com/caddy-dns/digitalocean
{
    email admin@example.com
}
 
*.example.com, example.com {
    tls {
        dns cloudflare {env.CF_API_TOKEN}
    }
    reverse_proxy localhost:3000
}

Ou en JSON pour plus de contrôle :

{
  "apps": {
    "tls": {
      "automation": {
        "policies": [{
          "subjects": ["*.example.com", "example.com"],
          "issuers": [{
            "module": "acme",
            "ca": "https://acme-v02.api.letsencrypt.org/directory",
            "email": "admin@example.com",
            "challenges": {
              "dns": {
                "provider": {
                  "name": "cloudflare",
                  "api_token": "{env.CF_API_TOKEN}"
                },
                "resolvers": ["1.1.1.1:53", "8.8.8.8:53"],
                "ttl": 120,
                "propagation_timeout": 120
              }
            }
          }]
        }]
      }
    }
  }
}

ZeroSSL comme CA alternative

Let's Encrypt est la CA par défaut. Caddy supporte ZeroSSL nativement comme fallback automatique :

{
    email admin@example.com
    acme_ca https://acme.zerossl.com/v2/DV90
}

Ou configurer les deux CA en parallèle — Caddy essaie l'une si l'autre échoue :

{
  "automation": {
    "policies": [{
      "subjects": ["example.com"],
      "issuers": [
        {
          "module": "acme",
          "ca": "https://acme-v02.api.letsencrypt.org/directory",
          "email": "admin@example.com"
        },
        {
          "module": "acme",
          "ca": "https://acme.zerossl.com/v2/DV90",
          "email": "admin@example.com"
        }
      ]
    }]
  }
}

On-demand TLS

On-demand TLS permet à Caddy d'obtenir des certificats pendant la première connexion TLS — pas au démarrage. Indispensable pour les plateformes multi-tenant qui hébergent des milliers de domaines clients.

{
    on_demand_tls {
        ask https://your-api.example.com/check-domain
        interval 2m
        burst 5
    }
}
 
# Catch-all qui déclenche on-demand TLS
:443 {
    tls {
        on_demand
    }
    reverse_proxy localhost:3000
}

Le endpoint ask est appelé avant chaque nouvelle obtention de certificat :

GET https://your-api.example.com/check-domain?domain=client-domaine.com
→ 200 : certificat autorisé
→ 4xx/5xx : refus

Implémentation Node.js / Next.js du endpoint ask :

app/api/check-domain/route.ts
import { NextRequest, NextResponse } from 'next/server'
 
export async function GET(req: NextRequest) {
  const domain = req.nextUrl.searchParams.get('domain')
  if (!domain) return NextResponse.json({ error: 'no domain' }, { status: 400 })
 
  // Vérifier que ce domaine est bien configuré dans votre DB
  const isValid = await db.domains.findFirst({
    where: { domain, active: true }
  })
 
  if (!isValid) return NextResponse.json({ error: 'domain not found' }, { status: 404 })
 
  return NextResponse.json({ ok: true })
}

PKI interne — CA privée

Pour les réseaux internes, les services Kubernetes, ou le mTLS entre microservices, Caddy peut être votre propre autorité de certification.

Configuration de la CA

{
  "apps": {
    "pki": {
      "certificate_authorities": {
        "local": {
          "name": "My Internal CA",
          "root": {
            "format": "pem_file",
            "certificate": "/etc/caddy/root.crt",
            "private_key": "/etc/caddy/root.key"
          },
          "intermediate": {
            "format": "pem_file",
            "certificate": "/etc/caddy/intermediate.crt",
            "private_key": "/etc/caddy/intermediate.key"
          }
        }
      }
    },
    "tls": {
      "automation": {
        "policies": [{
          "subjects": ["*.internal.example.com", "internal.example.com"],
          "issuers": [{"module": "internal", "ca": "local"}],
          "key_type": "p256"
        }]
      }
    }
  }
}

Générer le root CA (si pas encore créé)

# Générer une clé EC P-256
openssl ecparam -name prime256v1 -genkey -noout -out root.key
 
# Certificat root auto-signé (10 ans)
openssl req -new -x509 -days 3650 \
  -key root.key \
  -out root.crt \
  -subj "/C=FR/O=My Org/CN=My Internal CA"
 
# Optionnel : clé intermédiaire (bonne pratique)
openssl ecparam -name prime256v1 -genkey -noout -out intermediate.key
openssl req -new -key intermediate.key -out intermediate.csr \
  -subj "/C=FR/O=My Org/CN=My Internal CA Intermediate"
openssl x509 -req -in intermediate.csr \
  -CA root.crt -CAkey root.key -CAcreateserial \
  -days 1825 -out intermediate.crt \
  -extensions v3_ca \
  -extfile <(printf "[v3_ca]\nbasicConstraints=CA:TRUE\nkeyUsage=keyCertSign,cRLSign")

Installer le root CA dans les navigateurs/OS

# Linux (Debian/Ubuntu)
cp root.crt /usr/local/share/ca-certificates/my-internal-ca.crt
update-ca-certificates
 
# macOS
sudo security add-trusted-cert -d -r trustRoot \
  -k /Library/Keychains/System.keychain root.crt
 
# Windows
certutil -addstore -f "ROOT" root.crt
 
# Via Admin API Caddy (installe dans le trust store du système)
curl -X POST localhost:2019/pki/ca/local/install

mTLS entre services

Avec la PKI Caddy, vous pouvez forcer le mTLS sur des routes spécifiques :

{
  "handle": [{
    "handler": "authentication",
    "providers": {
      "mtls": {
        "trusted_ca_pool": ["/etc/caddy/root.crt"],
        "any_subject": true
      }
    }
  }, {
    "handler": "reverse_proxy",
    "upstreams": [{"dial": "backend:8080"}]
  }]
}

Policies TLS avancées

TLS 1.3 uniquement avec ciphers custom

{
  "tls_connection_policies": [{
    "match": {"sni": ["secure.example.com"]},
    "protocol_min": "tls1.3",
    "cipher_suites": [
      "TLS_AES_128_GCM_SHA256",
      "TLS_AES_256_GCM_SHA384",
      "TLS_CHACHA20_POLY1305_SHA256"
    ],
    "curves": ["x25519", "secp256r1"]
  }]
}

Certificat manuel (sans ACME)

{
  "apps": {
    "tls": {
      "certificates": {
        "load_files": [{
          "certificate": "/etc/ssl/custom/cert.pem",
          "key": "/etc/ssl/custom/key.pem",
          "tags": ["custom-cert"]
        }]
      }
    }
  }
}

Debugging TLS

# Vérifier les certificats gérés
curl -s localhost:2019/config/apps/tls | jq '.certificates'
 
# Forcer le renouvellement d'un certificat
curl -X DELETE "localhost:2019/config/apps/tls/certificates/automate" \
  -H "Content-Type: application/json" \
  -d '["example.com"]'
curl -X POST "localhost:2019/config/apps/tls/certificates/automate" \
  -H "Content-Type: application/json" \
  -d '["example.com"]'
 
# Tester la configuration TLS
openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | \
  openssl x509 -noout -dates -subject -issuer
 
# Logs Caddy filtrés sur TLS
journalctl -u caddy -f | grep -i "tls\|cert\|acme"

La stack TLS est maîtrisée. Chapitre suivant : reverse proxy avancé — load balancing, health checks actifs/passifs, transforms de headers, retries et circuit breaker.

Précédent
Admin API — configuration dynamique en JSON
Suivant
Reverse proxy avancé — load balancing, health checks, transforms

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