Un site qui ne fait rien, servi par quatre composants
Ce site ne fait rien. C'est un compliment. Du HTML généré au build, du CSS, quelques polices, des images. Pas une ligne de code exécutée côté serveur, pas de base de données, pas de session.
Jusqu'au 28 septembre 2026, chaque requête traversait pourtant quatre composants : le proxy Cloudflare, un conteneur Docker déployé par Plateforme auto-hébergée de déploiement d'applications, une alternative libre à Heroku ou Vercel, un proxy Proxy HTTP de la CNCF, utilisé ici pour réécrire les URL, poser les en-têtes de sécurité et écrire les logs d'accès sur le port 3000, puis un serveur busybox httpd sur le port 8080. Quatre composants pour servir des fichiers qui ne changent qu'au déploiement.
Le site sert désormais depuis Fonctionnalité de Cloudflare Workers qui sert des fichiers statiques depuis le réseau de Cloudflare. Un Worker « assets-only » ne contient aucun script : il n'y a rien à exécuter. Plus de serveur, plus de conteneur, plus de proxy à maintenir. Voici ce que la migration a vraiment demandé, ce qui a cassé en route et ce qu'elle coûte.
Avant, après : le chemin d'une requête
Le schéma dit l'essentiel. Avant, Cloudflare servait déjà de porte d'entrée : TLS, cache, protection, Service Zero Trust de Cloudflare qui exige une authentification avant d'atteindre un nom d'hôte. Il relayait ensuite chaque requête jusqu'au serveur. Après, le trajet s'arrête à la porte d'entrée.
- NavigateurHTTPS
- Cloudflareproxy, TLS, Access
- VPS Coolifyconteneur Docker
- Envoy:3000 en-têtes, logs
- busybox httpd:8080 fichiers
- NavigateurHTTPS
- CloudflareTLS, Access, en-têtes, fichiers
Côté facture, la documentation de Cloudflare est explicite : les requêtes vers des fichiers statiques sont gratuites et illimitées, sur l'offre gratuite comme sur l'offre payante. Seules les requêtes qui exécutent un script Worker comptent. Ici, il n'y en a aucune.
Qui remplace quoi
Envoy faisait cinq choses. Chacune a trouvé un équivalent, sauf une.
| Rôle d'Envoy | Équivalent Workers | Fichier |
|---|---|---|
En-têtes de sécurité (CSP, HSTS, nosniff...) |
Règles d'en-têtes par chemin | _headers |
| Cache long sur les CSS et JS versionnés | Cache-Control: immutable par chemin |
_headers |
URL propres (/blog/<slug> vers <slug>.html) |
html_handling: auto-trailing-slash |
wrangler.jsonc |
Anciennes URL .html redirigées |
Redirections 301 statiques | _redirects |
| Page 404 | not_found_handling: 404-page |
wrangler.jsonc |
| Logs d'accès JSON | Aucun |
La configuration de service passe de 226 à 73 lignes. Le gros du volume, c'était Envoy : 183 lignes de YAML pour des réécritures et des en-têtes.
Le fichier de configuration du Worker tient sur un écran. Il ne déclare aucun script, seulement un dossier à servir et deux comportements :
{
"name": "expertiseby-me",
"compatibility_date": "2026-09-28",
"assets": {
"directory": "./dist",
// foo.html est servi sur /foo, foo/index.html sur /foo/
"html_handling": "auto-trailing-slash",
// Les chemins inconnus reçoivent dist/404.html avec un statut 404
"not_found_handling": "404-page",
},
}
Les en-têtes se déclarent par motif de chemin. Les règles qui correspondent à une même requête se combinent, d'où une précaution : ne jamais poser Cache-Control sur /*, sinon il s'ajoute à celui des fichiers versionnés.
/*
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'self'; ...
/style.css
Cache-Control: public, max-age=31536000, immutable
Ce qui a cassé (et comment)
Les images OG sans ImageMagick
Chaque article a une Image affichée quand un lien est partagé sur LinkedIn, Slack ou ailleurs, déclarée par la balise og:image, générée au build. L'image Docker les dessinait avec ImageMagick. L'image de build de Workers Builds (Ubuntu 24.04) ne l'embarque pas.
Le script de build dessine donc désormais la même mise en page avec Pillow, la bibliothèque d'images de Python. Ce chemin de code n'avait jamais tourné nulle part. Il a été vérifié au pixel plutôt qu'à l'œil : logo à la valeur maximale de 255 (blanc pur), titres centrés à 3 pixels près, pied de page à un contraste de 6,3:1, conforme au niveau AA.
Les URL à barre oblique finale
Certaines pages vivent sous une URL qui finit par une barre oblique : /applications/, /consultant-devops-lille/. Envoy les réécrivait vers le bon fichier. Avec auto-trailing-slash, Workers sert applications/index.html sur /applications/ et redirige /applications vers la version avec barre oblique.
Le build écrit donc ces pages sous la forme <nom>/index.html. Les anciennes URL en .html, encore présentes dans des liens externes, partent en redirection 301 via _redirects.
Les logs d'accès disparaissent
C'est la seule fonction d'Envoy sans équivalent. Une requête servie depuis les fichiers statiques n'invoque aucun Worker : elle n'apparaît donc pas dans les logs de Workers. Il reste les statistiques HTTP de la zone Cloudflare, agrégées. Plus de ligne JSON par requête.
Pour un site vitrine, c'est acceptable. Pour un service soumis à une obligation de traçabilité, ce serait bloquant.
Mesurer l'audience sans serveur
La mesure d'audience reposait sur Outil d'analyse d'audience open source, auto-hébergeable et sans cookie, auto-hébergé sur un sous-domaine. Son script était injecté au build avec un identifiant client, et des attributs data-track suivaient les clics sur les boutons de prise de rendez-vous.
Avec un hébergement sans serveur, garder une instance OpenPanel revenait à garder un serveur pour une seule fonction. Elle a laissé la place à Cloudflare Web Analytics en configuration automatique : Cloudflare injecte lui-même son script dans le HTML servi sur le nom d'hôte, et les mesures repartent vers /cdn-cgi/rum, sur le même domaine. Aucune variable de build, aucun extrait de code dans le dépôt, et la CSP autorisait déjà son script.
| Critère | OpenPanel auto-hébergé | Cloudflare Web Analytics |
|---|---|---|
| Cookies | Aucun | Aucun, ni stockage local |
| Serveur à maintenir | Oui | Non |
| Suivi des clics (CTA) | Oui, via data-track |
Non |
| Conservation | Fixée par l'instance | 6 mois consultables, données brutes 7 jours |
| Lieu de traitement | Mon infrastructure | Cloudflare, Inc. (États-Unis) |
Le vrai renoncement est le suivi des clics : savoir quel bouton mène à une prise de rendez-vous. Le reste est un gain net.
Le volet juridique que tout le monde oublie
Changer d'hébergeur, c'est aussi changer ses mentions légales. L'article 6-III-1 de la Loi pour la confiance dans l'économie numérique du 21 juin 2004, qui impose notamment d'identifier l'éditeur et l'hébergeur d'un site impose d'identifier l'hébergeur : nom, adresse, téléphone. L'ancien texte désignait Hetzner, en Allemagne. Le nouveau désigne Cloudflare, Inc., à San Francisco.
La politique de confidentialité suit le même chemin. Elle affirmait un hébergement « intégralement situé dans l'Union européenne ». Ce n'est plus vrai : les requêtes, les données de connexion et les mesures d'audience peuvent être traitées hors de l'UE, dans le cadre du Data Privacy Framework UE-États-Unis et des clauses contractuelles types. La page le dit désormais.
Rien de spectaculaire. Mais un site qui promet une chose dans sa politique de confidentialité et en fait une autre dans son infrastructure a un problème, même s'il ne sert que des fichiers statiques.
La bascule DNS, et comment revenir en arrière
Un Worker se rattache à un nom d'hôte par un domaine personnalisé. Cloudflare crée alors l'enregistrement DNS et le certificat. Il refuse en revanche de le faire si le nom porte déjà un enregistrement CNAME. La bascule tient donc en quatre étapes :
- Noter l'enregistrement
wwwexistant : type, cible, statut du proxy. C'est tout le plan de retour arrière. - Le supprimer. Le site est injoignable à partir de cet instant.
- Ajouter
www.expertiseby.mecomme domaine personnalisé du Worker. - Vérifier les pages, les redirections et les en-têtes.
Le retour arrière consiste à recréer l'enregistrement noté. Le conteneur Coolify reste en service tant que la nouvelle version n'a pas fait ses preuves.
Deux détails méritent l'attention. D'abord, Cloudflare Access s'applique au nom d'hôte, quel que soit ce qui sert derrière : une application Access existante continue de s'appliquer sans modification. Ensuite, l'apex. _redirects ne sait pas filtrer par nom d'hôte, la redirection de expertiseby.me vers www passe donc par une règle de redirection de la zone, adossée à un enregistrement proxifié. Juste après la bascule, l'apex répondait 200 au lieu de rediriger. Le genre de détail qu'on ne voit qu'en le testant.
Vérifier à trois niveaux
Chaque niveau ajoute une couche de réalisme :
- Un serveur local qui imite le routage de Workers (
dev_server.py --cloudflare) : rapide, lancé aussi en intégration continue. wrangler dev, qui sert le dossier avec le vrai routeur de fichiers de Cloudflare.- L'URL
*.workers.devaprès un premier déploiement, marquéenoindexpour rester hors des moteurs de recherche.
Le même jeu de contrôles tourne à chaque niveau, puis une dernière fois sur le domaine de production :
check() {
status=$(curl -s -o /dev/null -w "%{http_code}" "$BASE$1")
echo "$1 -> $status (attendu $2)"
[ "$status" = "$2" ]
}
check / 200
check /applications 307
check /applications.html 301
check /page-qui-n-existe-pas 404
curl -sI "$BASE/" | grep -iE '^(content-security-policy|strict-transport-security)'
- /200
- /blog200
- /applications/200
- /applications307
- /applications.html301
- /consultant-devops-lille.html301
- /consultant-devops-lille/200
- /sre-freelance-hauts-de-france/200
- /mentions-legales/200
- /politique-de-confidentialite/200
- /robots.txt200
- /sitemap.xml200
- /og-default.png200
- /favicon.ico200
- /fonts/NerdSymbols-Subset.woff2200
- /chemin-inconnu404
- /blog/backstage-idp-startup-guide-pratique200
- /blog/og/backstage-idp-startup-guide-pratique.png200
- /blog/memoire-persistante-agents-ia-pgvector200
- /blog/og/memoire-persistante-agents-ia-pgvector.png200
- /blog/opentelemetry-ecosysteme-observabilite200
- /blog/og/opentelemetry-ecosysteme-observabilite.png200
- /blog/paseo-piloter-agents-ia-distance200
- /blog/og/paseo-piloter-agents-ia-distance.png200
- /blog/serveurs-gpu-bare-metal-llm-local200
- /blog/og/serveurs-gpu-bare-metal-llm-local.png200
Le cycle complet est court. Le build génère le site en moins d'une seconde, et wrangler n'envoie que les fichiers modifiés depuis la version précédente.
Ce que sert vraiment le site
Un dernier graphique, pour garder les pieds sur terre. Le site entier pèse 1,1 Mo, dont près d'un tiers de HTML et plus d'un quart de polices. C'est ce volume-là qui justifiait un conteneur, un proxy et un serveur.
Le vrai coût
La migration supprime un serveur, un conteneur, un proxy et un outil d'analytics à maintenir. Elle remplace 226 lignes de configuration par 73. Elle ramène le coût des requêtes à zéro.
Elle a aussi un prix, qu'il faut regarder en face :
- La concentration. DNS, proxy, contrôle d'accès, hébergement et mesure d'audience chez un seul fournisseur. Le jour où Cloudflare tombe, tout tombe ensemble. C'était déjà en partie le cas avant, puisque tout le trafic passait par son proxy.
- Les logs bruts. Il n'y a plus de ligne par requête. Pour enquêter sur un comportement précis, il faut se contenter d'agrégats.
- L'hébergement hors UE. Pour un site vitrine sans données personnelles côté serveur, c'est un point à documenter. Pour certains clients publics ou réglementés, c'est un critère éliminatoire.
Ma recommandation est simple. Si le site est réellement statique et passe déjà par Cloudflare, Workers static assets s'impose : le serveur ne servait qu'à relayer ce que Cloudflare sait servir lui-même. Si vous avez besoin de logs d'accès exhaustifs, d'un hébergement contractuellement européen ou de la moindre logique côté serveur, gardez votre conteneur, ou préparez-vous à ajouter un script Worker et à payer ce qu'il consomme.
Dans tous les cas, prévoyez une heure pour les mentions légales. C'est la partie de la migration que personne ne teste.
Vous voulez alléger l'infrastructure d'un site ou d'une plateforme sans rien casser en production ? Parlons-en.