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.
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.
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
}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# 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
# }
# ]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.jsonSi la nouvelle configuration est invalide, Caddy rejette la requête et garde l'ancienne — l'erreur est retournée dans la réponse HTTP.
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 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"
}
}
}
]
}
]
}
]
}'# 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"# 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
}
}
}'# 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"}]
}'Pour les applications qui pilotent Caddy programmatiquement :
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.