Servir le site avec Caddy et HTTPS

Caddy installé depuis son dépôt officiel, qui sert une page d'attente depuis la racine web en HTTPS, avec un certificat Let's Encrypt qu'il renouvelle tout seul. www et le HTTP simple redirigent vers le domaine nu (sans rien devant), HTTP/3 est actif, les en-têtes de sécurité et le cache sont réglés, les logs d'accès tournent.

intermédiaire~20 min de manipulation
#caddy#https#lets-encrypt#web-server#debian

Pas encore validée de bout en bout — sois le premier.Signaler un problème

Brouillon — pas encore exécuté de bout en bout. Cette page est écrite mais son auteur ne l'a pas encore déroulée sur une vraie machine. Des commandes peuvent être fausses : lis avant de lancer, et dis-nous ce qui casse.

En un coup d'œilUn serveur web, un certificat, un lien
Ton VPS
HTTPSwww → 301 → servesloadsACME
Visiteurhttps://
Let's EncryptACME
CaddyHTTPS · HTTP/3
Caddyfile/etc/caddy/Caddyfile
current/current

Les visiteurs atteignent Caddy sur le 443 (et le 80, qui ne fait que rediriger). Caddy obtient et renouvelle son certificat auprès de Let's Encrypt tout seul, et sert la release sur laquelle pointe le lien current.

Le serveur est verrouillé et le domaine pointe dessus. Cette page met un serveur web devant : elle ouvre les ports web, installe Caddy depuis son dépôt officiel, crée la racine web avec une page d'attente, écrit un seul Caddyfile, et vérifie depuis ton portable que https:// répond avec un certificat valide. La page suivante remplace la page d'attente par le vrai site.

Avant de commencer

Vérification
$dig +short A 
Retour attendu

Ouvrir les ports web

Serveur·
$sudo ufw allow 80/tcp
$sudo ufw allow 443/tcp
$sudo ufw allow 443/udp

Installer Caddy

Serveur·
$sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl gnupg
$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
$sudo chmod o+r /usr/share/keyrings/caddy-stable-archive-keyring.gpg
$sudo chmod o+r /etc/apt/sources.list.d/caddy-stable.list
$sudo apt update && sudo apt install -y caddy
Vérification
$systemctl is-active caddy
Retour attendu
active

Créer la racine web et une page d'attente

Serveur·
$sudo install -d -m 755 /releases/placeholder
$echo '<!doctype html><meta charset="utf-8"><title></title><p> est en cours de mise en place. Revenez bientôt.</p>' | sudo tee /releases/placeholder/index.html
$echo '<!doctype html><meta charset="utf-8"><title>Introuvable</title><p>404 : rien ici.</p>' | sudo tee /releases/placeholder/404.html
$sudo ln -sfn releases/placeholder /current

Écrire le Caddyfile

Le bloc ci-dessous est le /etc/caddy/Caddyfile en entier, déjà réglé selon les deux choix de cette page, HSTS et masquage des IP dans les logs : change-les dans le panneau et le fichier suit. Copie la commande et colle-la dans le terminal. Elle remplace le fichier par défaut d'un coup ; il n'y a rien à y ajouter ensuite.

Serveur·écrit un fichier/etc/caddy/Caddyfile
{
email
}
www. {
redir https://{uri} permanent
}
{
root * /current
encode zstd gzip
file_server
handle_errors 404 {
rewrite * /404.html
file_server
}
header {
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
X-Frame-Options "DENY"
Permissions-Policy "camera=(), microphone=(), geolocation=()"
-Server
}
@static path /_next/static/*
header @static Cache-Control "public, max-age=31536000, immutable"
@media path *.jpg *.jpeg *.png *.webp *.avif *.svg *.mp3 *.opus *.wav *.mp4
header @media Cache-Control "public, max-age=604800"
header Strict-Transport-Security "max-age=86400"
log {
output file /var/log/caddy/access.log {
roll_size 10MiB
roll_keep 10
}
format filter {
request>remote_ip ip_mask 16 32
request>client_ip ip_mask 16 32
}
}
}

HSTS. La ligne Strict-Transport-Security du fichier suit ton choix.

Valider et recharger

Serveur·
$sudo -u caddy caddy validate --config /etc/caddy/Caddyfile
$sudo systemctl reload caddy
$sudo journalctl -u caddy -f
Si le certificat échoue

Lis d'abord la ligne d'erreur dans le journal : elle nomme la cause. Ces requêtes, depuis ton portable, couvrent le côté DNS. Par ordre de probabilité :

Mac
$dig +short A
$dig +short A www.
$dig +short AAAA
$dig +short CAA
  • DNS : les deux requêtes A doivent répondre . Juste après un changement, attends que l'ancien TTL expire.
  • AAAA faux : si la requête AAAA répond quelque chose, ce doit être . Let's Encrypt essaie l'IPv6 en premier, donc un AAAA périmé fait échouer la validation même quand l'IPv4 est parfaite. Pas d'AAAA du tout, ça va.
  • Port 80 fermé : sudo ufw status doit montrer 80/tcp ALLOW ; si le Network Firewall d'OVH est actif, ouvre le 80 là-bas aussi.
  • CAA : la requête CAA doit être vide ou contenir letsencrypt.org. Quand Let's Encrypt échoue, Caddy se rabat sur ZeroSSL, dont l'identifiant CAA est sectigo.com.
  • Limites de débit : Let's Encrypt accepte 5 validations échouées par nom d'hôte et par heure. Pendant que tu cherches, fais pointer Caddy vers la CA de test (staging) : ouvre le fichier à la main sur le serveur,
Serveur·
$sudo vim /etc/caddy/Caddyfile

puis ajoute cette ligne dans le bloc d'options globales, en haut, sous email, et valide puis recharge comme plus haut :

text
acme_ca https://acme-staging-v02.api.letsencrypt.org/directory

Les certificats de staging ne sont pas reconnus par les navigateurs, donc curl va râler : c'est normal. Une fois le succès affiché dans le journal, recolle la commande du Caddyfile de l'étape précédente (elle réécrit le fichier sans la ligne), puis valide et recharge : Caddy range les certificats par CA et en demande un vrai.

Vérifier depuis ton portable

Vérification
$curl -sI https:/// | head -1
Retour attendu
HTTP/2 200
Vérification
$curl -sI https://www./ | head -1
Retour attendu
HTTP/2 301
Vérification
$curl -sI https://www./ | grep -i '^location'
Retour attendu
location: https:///
Vérification
$curl -s -o /dev/null -w '%{http_code}' https:///nope/
Retour attendu
404
Vérification
$curl -sI https:/// | grep -ci '^strict-transport-security'
Retour attendu
1

Terminé

répond maintenant en HTTPS avec un certificat Let's Encrypt que Caddy renouvelle tout seul, bien avant l'expiration. www et le HTTP simple redirigent vers https://, HTTP/3 est disponible, les ressources statiques sont mises en cache pour longtemps, et le log d'accès tourne tout seul avec des adresses masquées. Les visiteurs voient la page d'attente.

La page suivante, Déployer des versions atomiques avec rsync, construit le site Next.js sur ton portable, l'envoie comme nouvelle release sous , et bascule le lien current pour la mettre en ligne.

Tout a fonctionné ?

Si tu as suivi cette page jusqu'au bout sur une vraie machine, dis-le. Ta validation est datée et enregistre ta stack : le prochain lecteur sur le même chemin sait que ça marche toujours.

Cette copie est en lecture seule. Pour dire que ça marche, ou que ça ne marche pas, ouvre une issue

Seuls tes choix de stack sont enregistrés, jamais tes valeurs. Le pseudo reste sur ce navigateur.