Aller au contenu
medium

Caddy : HTTPS automatique et configuration simple

18 min de lecture

Logo Caddy

Caddy est un serveur web moderne avec HTTPS automatique par défaut. Contrairement à Nginx ou Apache, Caddy obtient et renouvelle vos certificats TLS sans configuration. En 5 minutes, vous pouvez servir un site statique sécurisé ou proxyfier une API.

Niveau : Débutant → Intermédiaire · Durée : 25-35 min

À la fin de ce guide, vous saurez :

  • Servir un site statique avec HTTPS automatique
  • Configurer un reverse proxy vers une application
  • Comprendre le modèle mental du Caddyfile
  • Déboguer les erreurs courantes (ACME, 502)
  • Utiliser tls internal pour les environnements LAN
CritèreCaddyNginx
HTTPSAutomatique (Let’s Encrypt intégré)Manuel (certbot séparé)
ConfigurationLisible (Caddyfile)Verbeux (nginx.conf)
Rechargementcaddy reload sans pertenginx -s reload
HTTP/3NatifModule externe
Courbe d’apprentissage30 min2-3 heures

En résumé : Caddy pour la simplicité, Nginx pour le contrôle fin et l’écosystème mature.

📜 Pour la culture : historique de Caddy

Caddy a été créé par Matt Holt en 2015 avec une vision claire : simplifier la configuration des serveurs web et rendre HTTPS accessible à tous. À l’époque, obtenir un certificat SSL était complexe et coûteux. L’intégration native de Let’s Encrypt dans Caddy a démocratisé HTTPS.

Aujourd’hui, Caddy est utilisé en production par de nombreuses entreprises pour sa simplicité et sa robustesse. Il est écrit en Go, ce qui lui confère d’excellentes performances et une facilité de déploiement (un seul binaire).

Avant de plonger dans la configuration, prenons 2 minutes pour comprendre comment Caddy pense. Cette compréhension vous évitera 90% des erreurs.

Imaginez un routeur entreprise avec des règles de routage :

  • Le site block (example.com { }) = une interface réseau (eth0, wan0)
  • Les handlers (root, file_server) = les règles de traitement du trafic
  • Le matcher (/api/*) = les ACL/filtres qui dirigent vers le bon backend
  • HTTPS automatique = le tunnel VPN configuré en auto-provisioning

Quand un paquet (requête HTTP) arrive, le routeur (matcher) l’oriente vers le bon backend (handler), et le tunnel TLS s’occupe du chiffrement automatiquement.

Modèle mental du Caddyfile : site block, handlers et HTTPS automatique

Pourquoi 4 couches ? Chaque requête traverse ces étapes dans l’ordre. Si vous comprenez ce flux, vous comprenez 90% de Caddy.

CoucheRôleAnalogieExemples
Site blockDéfinit le domaine ou l’adresseInterface réseau (quelle interface écoute ?)example.com { }, :8080 { }, localhost { }
MatchersFiltrent les requêtes (chemin, méthode, headers)ACL/règles de filtrage (quel trafic router ?)/api/*, @websocket, path /static/*
HandlersTraitent la requêteRègles de routage (où envoyer le paquet)file_server, reverse_proxy, respond, redir
RéponseHTTPS activé par défaut si domaine publicAuto-provisioning TLS (tunnel sécurisé auto)TLS auto via Let’s Encrypt
Fenêtre de terminal
# Ajouter le dépôt officiel
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
# Installer
sudo apt update && sudo apt install caddy
# Le service systemd est configuré automatiquement
sudo systemctl status caddy

Vérification :

Fenêtre de terminal
caddy version
# v2.8.x
# Le Caddyfile par défaut est dans /etc/caddy/Caddyfile
cat /etc/caddy/Caddyfile
  1. Créer le répertoire et le fichier

    Fenêtre de terminal
    sudo mkdir -p /var/www/monsite
    echo '<h1>Hello Caddy!</h1>' | sudo tee /var/www/monsite/index.html
    sudo chown -R caddy:caddy /var/www/monsite
  2. Configurer le Caddyfile

    Fenêtre de terminal
    sudo tee /etc/caddy/Caddyfile << 'EOF'
    # Pour un test local (pas de TLS)
    :8080 {
    root * /var/www/monsite
    file_server
    }
    EOF
  3. Recharger Caddy

    Fenêtre de terminal
    sudo systemctl reload caddy
  4. Tester

    Fenêtre de terminal
    curl http://localhost:8080
    # <h1>Hello Caddy!</h1>

Si votre domaine pointe vers votre serveur :

monsite.com {
root * /var/www/monsite
file_server
}

C’est tout. Caddy :

  • Obtient automatiquement un certificat Let’s Encrypt
  • Redirige HTTP → HTTPS
  • Renouvelle le certificat avant expiration

Pour React, Vue, Angular, etc. — fallback vers index.html :

monsite.com {
root * /var/www/spa
try_files {path} /index.html
file_server
}

Un reverse proxy est un intermédiaire entre vos utilisateurs et votre application. C’est comme un réceptionniste d’hôtel :

  • Le client ne parle jamais directement aux employés (backend)
  • Le réceptionniste (Caddy) reçoit la demande et la transmet au bon service
  • Le réceptionniste ajoute des informations utiles (“ce client vient de la chambre 42”)

Pourquoi utiliser un reverse proxy ?

Sans reverse proxyAvec reverse proxy (Caddy)
Votre app Node/Python écoute sur le port 80Caddy écoute sur 80/443, votre app sur 3000
Pas de HTTPS (ou config manuelle)HTTPS automatique
Une seule app par serveurPlusieurs apps sur le même serveur
Votre app gère les logs HTTPCaddy centralise les logs
api.example.com {
reverse_proxy localhost:3000
}

C’est tout. En une ligne, Caddy :

  • Écoute sur api.example.com ports 80 et 443
  • Obtient un certificat HTTPS
  • Transmet les requêtes à votre app sur localhost:3000
  • Ajoute automatiquement les headers Host, X-Forwarded-For, X-Forwarded-Proto
example.com {
# L'API sur /api/*
reverse_proxy /api/* localhost:3000
# Le reste en statique
root * /var/www/frontend
file_server
}
example.com {
reverse_proxy /ws/* localhost:3000
}

Caddy gère automatiquement l’upgrade WebSocket. Pas de configuration spéciale.

example.com {
reverse_proxy {
to backend1:3000
to backend2:3000
to backend3:3000
lb_policy round_robin
health_uri /health
health_interval 10s
}
}

Quand vous écrivez example.com { } dans votre Caddyfile, Caddy :

  1. Détecte que c’est un domaine public (pas localhost, pas une IP)
  2. Contacte Let’s Encrypt pour prouver que vous contrôlez le domaine
  3. Obtient un certificat valide 90 jours
  4. Configure TLS avec les bonnes options de sécurité
  5. Crée une redirection HTTP → HTTPS automatique
  6. Renouvelle le certificat avant expiration (environ 30 jours avant)

Tout cela sans une seule ligne de configuration TLS.

ModeQuand l’utiliserConfiguration
Auto (défaut)Domaine public accessibleexample.com { ... }
InternalRéseau local / développementtls internal
ManuelCertificats existantstls /path/cert.pem /path/key.pem

Pour un serveur interne sans accès Internet :

intranet.local {
tls internal
reverse_proxy localhost:3000
}

Caddy génère un certificat auto-signé. Important : vous devez installer la CA Caddy dans les navigateurs/clients pour éviter les avertissements de sécurité.

Fenêtre de terminal
# Afficher le chemin de la CA
caddy trust
# Installe la CA dans le trust store système (Linux/macOS)

Si les ports 80/443 ne sont pas accessibles (firewall, NAT), utilisez le DNS Challenge :

Fenêtre de terminal
# Compiler Caddy avec le plugin DNS (ex: Cloudflare)
xcaddy build --with github.com/caddy-dns/cloudflare
*.example.com {
tls {
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
reverse_proxy localhost:3000
}
example.com {
root * /var/www/html
file_server
handle_errors {
@404 expression {err.status_code} == 404
handle @404 {
root * /var/www/errors
rewrite * /404.html
file_server
}
@5xx expression {err.status_code} >= 500
handle @5xx {
respond "Service temporairement indisponible" 503
}
}
}
example.com {
root * /var/www/html
file_server
}
example.com {
root * /var/www/app
try_files {path} /index.html
file_server
}
api.example.com {
reverse_proxy localhost:3000
}
example.com {
handle /api/* {
reverse_proxy localhost:3000
}
handle {
root * /var/www/frontend
file_server
}
}
example.com {
handle_path /api/* {
reverse_proxy localhost:3000
}
}
intranet.local {
tls internal
reverse_proxy localhost:3000
}
example.com {
tls /etc/ssl/cert.pem /etc/ssl/key.pem
file_server
}
old.example.com {
redir https://new.example.com{uri} permanent
}
example.com {
header {
Strict-Transport-Security "max-age=31536000"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
}
file_server
}
example.com {
basicauth /admin/* {
admin $2a$14$hash...
}
file_server
}

Pour générer un hash de mot de passe :

Fenêtre de terminal
caddy hash-password
# Entrez le mot de passe, Caddy retourne le hash bcrypt
{
servers {
metrics
}
}
example.com {
file_server
}
ErreurConséquenceSolution
DNS ne pointe pas vers le serveurHTTPS auto échoue (erreur ACME)dig example.com pour vérifier
Ports 80/443 fermésLet’s Encrypt ne peut pas validerOuvrir les ports ou utiliser DNS Challenge
Domaine local sans tls internalCaddy tente d’obtenir un certificatAjouter tls internal pour .local, .lan
Oublier handle_path pour stripBackend reçoit /api/users au lieu de /usersUtiliser handle_path /api/* { ... }
Ordre des handlers incorrectUn handler large capture toutPlacer /api/* avant file_server catch-all
Backend downCaddy renvoie 502curl localhost:3000 pour confirmer
https:// dans reverse_proxyErreurs de connexionNe pas mettre https:// sauf si backend l’exige
Fenêtre de terminal
# Valider la configuration
caddy validate --config /etc/caddy/Caddyfile
# Recharger sans downtime
sudo systemctl reload caddy
# Voir les logs
sudo journalctl -u caddy -f
# Formater le Caddyfile (indentation)
caddy fmt --overwrite /etc/caddy/Caddyfile
ErreurCause probableSolution
ACME challenge failedDNS incorrect ou ports fermésVérifier DNS + ouvrir 80/443
502 Bad GatewayBackend ne répond pascurl localhost:3000 pour tester
too many redirectsBoucle HTTP ↔ HTTPSVérifier la config proxy/CDN
permission deniedCaddy ne peut pas lire les fichierschown -R caddy:caddy /var/www
address already in useAutre service sur le portss -tlnp | grep :80
Fenêtre de terminal
# Voir les détails de l'erreur
sudo journalctl -u caddy | grep -i acme
# Erreurs typiques :
# - "no valid IP addresses" → DNS pas configuré
# - "connection refused" → port 80 fermé
# - "rate limit" → trop de tentatives (attendre 1h)
Fenêtre de terminal
# 1. Le backend tourne-t-il ?
curl -I http://localhost:3000
# 2. Caddy peut-il joindre le backend ?
sudo -u caddy curl http://localhost:3000
# 3. Regarder les logs
sudo journalctl -u caddy -f
example.com {
log {
output file /var/log/caddy/access.log {
roll_size 100mb
roll_keep 5
}
format json
}
file_server
}

Ajoutez dans le bloc global :

{
servers {
metrics
}
}
example.com {
file_server
}

Les métriques sont exposées sur :2019/metrics par défaut.

Fenêtre de terminal
curl localhost:2019/metrics
DirectiveDescription
root * /pathDéfinir la racine des fichiers
file_serverServir les fichiers statiques
file_server browseAvec listing des répertoires
try_files {path} /index.htmlFallback SPA
DirectiveDescription
reverse_proxy localhost:3000Proxy simple
reverse_proxy /api/* localhost:3000Proxy sur un chemin
handle_path /api/* { reverse_proxy ... }Strip le préfixe
DirectiveDescription
tls email@example.comHTTPS auto avec email
tls internalCertificat auto-signé (LAN)
tls /cert.pem /key.pemCertificats manuels
DirectiveDescription
header X-Custom "value"Ajouter un header
header -ServerSupprimer un header
basicauth /admin/* { user hash }Auth basique
encode gzip zstdCompression
DirectiveDescription
redir /old /new permanentRedirection 301
rewrite /old /newRéécriture interne
respond "OK" 200Réponse directe
handle_errors { ... }Pages d’erreur custom
CommandeDescription
caddy validate --config /etc/caddy/CaddyfileValider la config
caddy fmt --overwrite CaddyfileFormater
caddy reloadRecharger la config
caddy hash-passwordGénérer un hash bcrypt
caddy trustInstaller la CA interne
  1. HTTPS automatique : fonctionne si DNS + ports 80/443 accessibles
  2. tls internal : pour les environnements LAN sans accès Internet
  3. caddy validate : toujours valider avant de recharger
  4. handle_path : pour strip le préfixe URL avant le backend
  5. Ordre des handlers : spécifique → générique
  6. Logs : journalctl -u caddy -f pour déboguer

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.