Héberger moi-même,
sans rien exposer d'inutile.
Plutôt qu'un hébergeur clé en main, je voulais un site qui soit lui-même une démonstration de ce que je sais faire :
- Auto-hébergé sur un serveur Debian chez moi, derrière une box grand public.
- HTTPS partout, avec un certificat Let's Encrypt renouvelé automatiquement.
- Surface d'attaque minimale : seul Caddy est joignable depuis Internet (port 443, et port 80 pour rediriger vers HTTPS), aucun service interne joignable.
- Un formulaire de contact qui fonctionne vraiment, sans service tiers côté visiteur.
- Reproductible : tout le déploiement tient dans un dépôt et une commande.
Un seul point d'entrée.
Le nom de domaine m3tsh.com (DNS chez IONOS) pointe vers l'IP publique de ma box. La box redirige le port 443 vers le port 4567 du serveur, où écoute Caddy, le seul conteneur publié. Le port 80 arrive aussi sur Caddy, qui se contente de rediriger vers HTTPS.
Caddy termine le TLS, ajoute les en-têtes de sécurité, sert le site statique et transmet les requêtes /api/* à l'API de contact. Cette API n'est joignable que sur le réseau Docker interne : elle n'a aucun port publié.
Deux conteneurs, une commande.
Tout est décrit dans un fichier Docker Compose ; la configuration sensible (domaine, token du bot) vit dans un fichier .env non versionné. Un docker compose up -d suffit à tout remonter.
# docker-compose.yml (résumé) caddy: image: caddy:2.10 ports: "4567:4567" # HTTPS (443 sur la box) "8080:8080" # HTTP → redirection HTTPS volumes: ./site:/srv/site:ro # site en lecture seule caddy_data:/data # certificats contact: build: ./contact # python:3.13-alpine expose: "8000" # réseau interne uniquement # tourne en non-root (uid 10001, défini dans le Dockerfile) volumes: messages:/data env: TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID
- Certificat : Caddy l'obtient seul via le défi TLS-ALPN-01, qui passe par le port 443. Le port 80 ne sert qu'à rediriger les visiteurs vers HTTPS.
- Domaine : www.m3tsh.com redirige en 301 vers m3tsh.com.
- Formulaire : l'API (Python, bibliothèque standard uniquement) enregistre chaque message dans un volume puis m'envoie une notification via l'API Bot de Telegram.
- Performance : compression zstd/gzip et cache navigateur sur les fichiers statiques.
Défense en profondeur,
même pour un petit site.
- Réseau : seuls les ports 443 et 80 sont redirigés, tous deux vers Caddy (le 80 ne fait que rediriger vers HTTPS) ; l'API n'est publiée sur aucun port de l'hôte.
- En-têtes HTTP : HSTS, Content-Security-Policy stricte (aucun script externe ni inline), X-Frame-Options, nosniff, Referrer-Policy, Permissions-Policy, en-tête Server masqué.
- Formulaire : corps limité à 16 Ko par Caddy, validation des champs côté serveur, champ piège anti-robot, limite de 5 messages par heure et par IP.
- Conteneurs : API exécutée sous un utilisateur non-root, site monté en lecture seule, aucune dépendance Python externe à maintenir.
- Secrets : fichier .env en droits 600, exclu du dépôt.
Mis en ligne en pilotant
un agent IA.
Ce site a été conçu et déployé en travaillant avec Claude Code, un agent IA qui agit directement dans le terminal du serveur : il lit les fichiers, exécute des commandes et propose des modifications, que je valide une par une.
Mon rôle : fixer les objectifs et les contraintes, faire les choix d'architecture, contrôler chaque action et vérifier le résultat. Quelques exemples concrets du déploiement :
- Passage à mon domaine : retrait du DNS dynamique, nouveau mode d'obtention du certificat sans ouvrir le port 80.
- Erreur TLS au lancement : diagnostic d'un cache DNS de la box qui pointait encore vers l'ancienne IP.
- Mails non reçus : le relais SMTP bloquait l'IP du serveur ; décision de passer à des notifications Telegram, plus simples et plus fiables.
- Affichage mobile : captures automatisées dans un navigateur headless pour repérer et corriger les défauts.
- Notifications absentes : configuration correcte mais conteneur relancé sans reconstruire son image, donc encore sur l'ancien code ; corrigé avec
docker compose up -d --build.
Savoir comment fonctionne un modèle de langage — contexte, outils, limites — permet de lui confier de vraies tâches d'administration tout en gardant la main.