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 4·35 min

Admin API — configuration dynamique en JSON

L'Admin API est ce qui distingue Caddy de tous les autres serveurs web. Toute la configuration est un objet JSON que vous pouvez lire et modifier en live via REST — sans fichier, sans reload, sans interruption de trafic. Ce chapitre couvre l'API complète avec des exemples réels.

Activer et sécuriser l'API

Par défaut, l'API écoute sur localhost:2019 — accessible uniquement en local. En production sur un serveur dédié, c'est acceptable. Pour un accès distant, configurez TLS :

{
    admin {
        listen 0.0.0.0:2019
        origins localhost:2019 10.0.0.0/8
        enforce_origin
 
        # TLS sur l'API admin
        tls /etc/caddy/admin-cert.pem /etc/caddy/admin-key.pem
    }
}

Pour une exposition plus sécurisée, proxy l'API derrière Caddy lui-même avec authentication :

caddy-api.example.com {
    forward_auth localhost:4180 {  # oauth2-proxy ou similaire
        uri /oauth2/auth
        copy_headers X-Auth-User
    }
    reverse_proxy localhost:2019
}

Endpoints principaux

GET    /config/                    Lire toute la config
POST   /load                       Remplacer toute la config
PATCH  /config/path/to/key         Modifier un chemin spécifique
PUT    /config/path/to/key         Remplacer une valeur à un chemin
DELETE /config/path/to/key         Supprimer une valeur
POST   /config/path/to/array/...   Ajouter à un tableau
GET    /id/:id                     Trouver un objet par son @id
PUT    /id/:id                     Remplacer un objet par son @id
PATCH  /id/:id                     Modifier un objet par son @id
 
GET    /reverse_proxy/upstreams    État des upstreams (health, pool)
POST   /pki/ca/:id/install         Installer le cert root dans le trust store

Lire la configuration

# Config complète
curl -s localhost:2019/config/ | jq .
 
# Section spécifique
curl -s localhost:2019/config/apps/http/servers/main/routes | jq .
 
# Upstreams d'un reverse proxy (santé, latence)
curl -s localhost:2019/reverse_proxy/upstreams | jq .
# [
#   {
#     "address": "localhost:3000",
#     "healthy": true,
#     "num_requests": 42,
#     "fails": 0
#   }
# ]

Remplacer toute la config — /load

L'endpoint /load remplace atomiquement toute la configuration. C'est l'équivalent de caddy reload mais via HTTP.

# Depuis un Caddyfile
curl -X POST localhost:2019/load \
  -H "Content-Type: text/caddyfile" \
  --data-binary @/etc/caddy/Caddyfile
 
# Depuis un JSON
curl -X POST localhost:2019/load \
  -H "Content-Type: application/json" \
  --data-binary @config.json

Si la nouvelle configuration est invalide, Caddy rejette la requête et garde l'ancienne — l'erreur est retournée dans la réponse HTTP.

Modifier une valeur avec @id

Pour modifier un objet précis sans toucher au reste, assignez-lui un @id dans votre configuration :

{
  "apps": {
    "http": {
      "servers": {
        "main": {
          "routes": [
            {
              "@id": "route-mon-app",
              "match": [{"host": ["mon-app.example.com"]}],
              "handle": [
                {
                  "@id": "proxy-mon-app",
                  "handler": "reverse_proxy",
                  "upstreams": [
                    {"dial": "localhost:3000"}
                  ]
                }
              ]
            }
          ]
        }
      }
    }
  }
}
# Lire cet objet spécifique
curl -s localhost:2019/id/proxy-mon-app | jq .
 
# Mettre à jour uniquement les upstreams
curl -X PATCH localhost:2019/id/proxy-mon-app \
  -H "Content-Type: application/json" \
  -d '{
    "handler": "reverse_proxy",
    "upstreams": [
      {"dial": "localhost:3001"},
      {"dial": "localhost:3002"},
      {"dial": "localhost:3003"}
    ]
  }'
 
# Désactiver temporairement en changeant le handler
curl -X PUT localhost:2019/id/proxy-mon-app \
  -H "Content-Type: application/json" \
  -d '{"handler": "static_response", "status_code": 503, "body": "Maintenance"}'

Ajouter une route dynamiquement

# Ajouter une nouvelle route au début du tableau (priorité haute)
curl -X POST "localhost:2019/config/apps/http/servers/main/routes/0" \
  -H "Content-Type: application/json" \
  -d '{
    "@id": "route-nouveau-service",
    "match": [{"host": ["nouveau.example.com"]}],
    "handle": [
      {
        "handler": "subroute",
        "routes": [
          {
            "handle": [
              {
                "handler": "reverse_proxy",
                "upstreams": [{"dial": "localhost:5000"}],
                "health_checks": {
                  "active": {
                    "uri": "/health",
                    "interval": "10s",
                    "timeout": "3s"
                  }
                }
              }
            ]
          }
        ]
      }
    ]
  }'

Supprimer une route

# Par @id
curl -X DELETE localhost:2019/id/route-nouveau-service
 
# Par index dans le tableau
curl -X DELETE "localhost:2019/config/apps/http/servers/main/routes/0"

Modifier les upstreams d'un load balancer

# Remplacer complètement la liste d'upstreams d'un proxy existant
curl -X PATCH localhost:2019/id/proxy-api \
  -H "Content-Type: application/json" \
  -d '{
    "handler": "reverse_proxy",
    "upstreams": [
      {"dial": "10.0.1.10:3000"},
      {"dial": "10.0.1.11:3000"},
      {"dial": "10.0.1.12:3000"}
    ],
    "load_balancing": {
      "selection_policy": {"policy": "least_conn"}
    },
    "health_checks": {
      "active": {
        "uri": "/health",
        "interval": "5s",
        "timeout": "2s",
        "expect_status": 200
      }
    }
  }'

Gestion des certificats TLS via API

# Déclencher l'obtention d'un certificat immédiatement
curl -X POST localhost:2019/config/apps/tls/certificates/automate \
  -H "Content-Type: application/json" \
  -d '["example.com", "www.example.com"]'
 
# Lire les certificats gérés
curl -s localhost:2019/config/apps/tls | jq .
 
# Ajouter une politique TLS pour un nouveau domaine
curl -X POST localhost:2019/config/apps/tls/automation/policies/0 \
  -H "Content-Type: application/json" \
  -d '{
    "subjects": ["internal.example.com"],
    "issuers": [{"module": "internal"}]
  }'

Client Go pour l'Admin API

Pour les applications qui pilotent Caddy programmatiquement :

caddy/client.go
package caddy
 
import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
)
 
type Client struct {
    BaseURL    string
    HTTPClient *http.Client
}
 
func NewClient(baseURL string) *Client {
    return &Client{
        BaseURL:    baseURL,
        HTTPClient: &http.Client{},
    }
}
 
func (c *Client) GetConfig(path string) (json.RawMessage, error) {
    resp, err := c.HTTPClient.Get(c.BaseURL + "/config/" + path)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    var result json.RawMessage
    return result, json.NewDecoder(resp.Body).Decode(&result)
}
 
func (c *Client) PatchID(id string, payload any) error {
    body, err := json.Marshal(payload)
    if err != nil {
        return err
    }
    req, _ := http.NewRequest("PATCH", fmt.Sprintf("%s/id/%s", c.BaseURL, id), bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    resp, err := c.HTTPClient.Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    if resp.StatusCode >= 400 {
        var errResp map[string]any
        json.NewDecoder(resp.Body).Decode(&errResp)
        return fmt.Errorf("caddy API error %d: %v", resp.StatusCode, errResp)
    }
    return nil
}

L'Admin API est maîtrisée. Le chapitre suivant plonge dans le système TLS : ACME, challenges DNS, wildcard certs, PKI interne, on-demand TLS et rotation de certificats.

Précédent
Caddyfile avancé — matchers, snippets, directives
Suivant
HTTPS et PKI — ACME, wildcards, on-demand TLS, CA interne

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