Aller au contenu

Caddyfile : exemples de directives réutilisables

Bibliothèque d’exemples de Caddyfile à adapter. Les domaines (exemple.com), chemins et ports sont fictifs. Après chaque modification :

Fenêtre de terminal
sudo caddy validate --config /etc/caddy/Caddyfile && sudo systemctl reload caddy

Pour installer Caddy, voir Installer Caddy sur Ubuntu. Les commandes sont dans les principales commandes Caddy.

Un snippet est un bloc nommé, défini une fois en haut du Caddyfile et inséré partout avec import. Les exemples ci-dessous sont écrits pour être utilisés ainsi.

(securite) {
header {
Strict-Transport-Security "max-age=31536000"
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
-Server
}
}
exemple.com {
import securite
# …
}
autre.exemple.com {
import securite
# …
}

Le bloc sans adresse, placé tout en haut du fichier, définit les réglages communs à tous les sites.

{
# E-mail de contact pour les certificats Let's Encrypt
email admin@exemple.com
# Protocoles : HTTP/1.1, HTTP/2 et HTTP/3
servers {
protocols h1 h2 h3
}
}
(securite) {
header {
# Force HTTPS pendant un an
Strict-Transport-Security "max-age=31536000"
# Empêche le navigateur de deviner les types de fichiers
X-Content-Type-Options "nosniff"
# Interdit l'affichage dans une iframe d'un autre site
X-Frame-Options "SAMEORIGIN"
# Limite les informations envoyées dans l'en-tête Referer
Referrer-Policy "strict-origin-when-cross-origin"
# Désactive des fonctions du navigateur inutiles
Permissions-Policy "camera=(), microphone=(), geolocation=()"
# Masque la version du serveur
-Server
}
}

Une CSP limite les origines autorisées pour les scripts, styles et images. Exemple strict pour un site statique sans ressources externes :

header Content-Security-Policy "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; frame-ancestors 'self'"

Testez toujours une CSP sur un site de test : une règle trop stricte bloque des fonctions du site. Les scripts en ligne exigent 'unsafe-inline' ou une empreinte.

Bloque l’accès aux fichiers commençant par un point (.env, .git…), sauf /.well-known/ utilisé par les certificats et certains services :

(fichiers-caches) {
@cachees {
path */.*
not path /.well-known/*
}
respond @cachees 404
}
request_body {
max_size 10MB
}

Générez d’abord un hash du mot de passe :

Fenêtre de terminal
caddy hash-password

Collez le résultat dans le Caddyfile. Ici seul le dossier /prive/ est protégé :

exemple.com {
root * /var/www/site
basic_auth /prive/* {
alice $2a$14$HASH_GENERE_PAR_CADDY_HASH_PASSWORD
bob $2a$14$AUTRE_HASH
}
file_server
}

Pour protéger tout le site, retirez le chemin : basic_auth { … }.

(reseau-local) {
@interdit not remote_ip 192.168.0.0/16 10.0.0.0/8 203.0.113.10
respond @interdit 403
}
admin.exemple.com {
import reseau-local
reverse_proxy 127.0.0.1:9000
}

Derrière un autre proxy (CDN, répartiteur de charge), remote_ip voit l’adresse du proxy : utilisez alors client_ip après avoir déclaré les proxys de confiance (trusted_proxies).

exemple.com {
root * /var/www/site
@prive path /prive/*
handle @prive {
@interdit not remote_ip 192.168.0.0/16
respond @interdit 403
basic_auth {
alice $2a$14$HASH
}
file_server
}
file_server
}
app.exemple.com {
reverse_proxy 127.0.0.1:3000
}

Caddy gère HTTPS, HTTP/2, HTTP/3 et les WebSockets sans configuration supplémentaire, et ajoute les en-têtes X-Forwarded-*.

(proxy-app) {
reverse_proxy 127.0.0.1:3000 {
# Transmet l'adresse du visiteur à l'application
header_up X-Real-IP {remote_host}
# Sans mise en mémoire tampon : utile pour le streaming et les événements serveur (SSE)
flush_interval -1
}
}

Par défaut, Caddy transmet l’en-tête Host du visiteur à l’application. Si elle exige son propre nom d’hôte, ajoutez header_up Host {upstream_hostport}.

handle_path retire le préfixe avant de transmettre la requête :

exemple.com {
root * /var/www/site
handle_path /api/* {
reverse_proxy 127.0.0.1:4000
}
file_server
}

Une requête /api/utilisateurs arrive à l’application sous la forme /utilisateurs.

app.exemple.com {
reverse_proxy 10.0.0.11:8080 10.0.0.12:8080 {
lb_policy round_robin
health_uri /sante
health_interval 10s
health_timeout 3s
}
}

Les serveurs qui ne répondent pas sur /sante sont retirés de la rotation jusqu’à leur retour.

reverse_proxy https://127.0.0.1:8443 {
transport http {
# À éviter en production : désactive la vérification du certificat
tls_insecure_skip_verify
}
}
exemple.com {
root * /var/www/site
encode zstd gzip
file_server
}

Renvoie index.html pour toutes les routes gérées côté navigateur :

exemple.com {
root * /var/www/site
encode zstd gzip
try_files {path} /index.html
file_server
}

Le statut HTTP reste 404, ce qui est correct pour le référencement :

(erreur-404) {
handle_errors {
@notfound `{err.status_code} == 404`
rewrite @notfound /404.html
file_server
}
}
# www vers le domaine nu
www.exemple.com {
redir https://exemple.com{uri} permanent
}
# Ancienne page vers la nouvelle
exemple.com {
redir /ancienne-page /nouvelle-page permanent
}
telechargements.exemple.com {
root * /var/www/fichiers
file_server browse
}
encode zstd gzip

Pour servir des fichiers déjà compressés (générés à l’avance par votre build), évitez la compression à la volée :

file_server {
precompressed zstd br gzip
}

Les fichiers dont le nom change à chaque version (avec une empreinte, comme app.a1b2c3.js) peuvent être gardés un an. Les pages HTML doivent être revalidées :

(cache) {
@assets path /assets/* /_astro/* *.woff2
header @assets Cache-Control "public, max-age=31536000, immutable"
@html {
path / *.html
}
header @html Cache-Control "no-cache"
}
exemple.com {
log {
output file /var/log/caddy/exemple.log {
roll_size 10MiB
roll_keep 5
}
format json
}
}

Le dossier de logs doit exister et appartenir à l’utilisateur caddy :

Fenêtre de terminal
sudo mkdir -p /var/log/caddy
sudo chown caddy:caddy /var/log/caddy

Assemblage des snippets dans un Caddyfile :

{
email admin@exemple.com
}
(securite) {
header {
Strict-Transport-Security "max-age=31536000"
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
-Server
}
}
(fichiers-caches) {
@cachees {
path */.*
not path /.well-known/*
}
respond @cachees 404
}
(erreur-404) {
handle_errors {
@notfound `{err.status_code} == 404`
rewrite @notfound /404.html
file_server
}
}
(cache) {
@assets path /assets/* /_astro/*
header @assets Cache-Control "public, max-age=31536000, immutable"
}
exemple.com {
root * /var/www/site
encode zstd gzip
import securite
import fichiers-caches
import cache
import erreur-404
basic_auth /prive/* {
alice $2a$14$HASH
}
handle_path /api/* {
reverse_proxy 127.0.0.1:4000
}
file_server
}
www.exemple.com {
redir https://exemple.com{uri} permanent
}
app.exemple.com {
import securite
reverse_proxy 127.0.0.1:3000
}