
Pendant des années, Vercel a été la plateforme sur laquelle nous déployions par défaut presque tout ce qui touchait au Web chez WebCatalog. La plupart de ces projets utilisaient Next.js : l’association allait donc de soi. Nos sites marketing, interfaces produit, paramètres de compte, console développeur, système d’authentification, outils d’administration et API se sont tous retrouvés sur une pile technique à peu près identique.
Cette approche a bien fonctionné pendant longtemps. Vercel simplifiait les déploiements, Next.js nous offrait un framework full-stack productif, et nous avions rarement à nous préoccuper de l’infrastructure sous-jacente.
Puis cette commodité a cessé de correspondre à la nature de nos produits.
Nos sites marketing tiraient toujours parti du SSR (rendu côté serveur), mais la plupart de nos autres applications Web n’en avaient pas besoin. De simples SPA (applications monopages) suffisaient. Nos API avaient besoin d’un environnement Node.js classique, avec des dépendances natives et des processus de longue durée. Presque tout le reste de notre pile frontend utilisait déjà Vite. Dans le même temps, les coûts d’infrastructure devenaient de plus en plus difficiles à ignorer, notamment avec la hausse du trafic public et automatisé.
Nous avons d’abord essayé d’optimiser l’existant. Nous avons envisagé de conserver Next.js et de le transférer sur Cloudflare Workers au moyen d’un adaptateur. Nous avons essayé Astro pour les sites marketing. Nous avons aussi brièvement hébergé nos API sur Cloudflare Containers.
Finalement, nous avons abouti à une solution plus simple : la plupart de nos applications Web utilisent désormais Vite + TanStack Router sur Cloudflare Workers, nos sites marketing utilisent TanStack Start avec SSR sur Cloudflare Workers, et nos API utilisent Hono + tRPC sur Railway, dans des conteneurs Node.js à exécution continue.
Cette migration a réduit nos coûts d’infrastructure Web d’environ 80 %. Après avoir également renforcé nos règles de sécurité Cloudflare et bloqué davantage de trafic automatisé hostile en périphérie du réseau, les économies totales sont passées à environ 90 %.
L’essentiel de la mise en œuvre de la migration a par ailleurs été réalisé par des agents de programmation IA, principalement Claude Fable 5 et Claude Opus 5. Les ingénieurs ont choisi l’architecture, défini les contraintes, passé les modifications en revue et vérifié le résultat.
Nous n’avons pas commencé par quitter Vercel
Notre premier réflexe a été d’optimiser la configuration existante.
webcatalog.io était le point de départ le plus évident : le site reçoit beaucoup de trafic public, alors que la plupart de ses pages changent relativement peu. Nous avons constaté qu’une trop grande partie du site faisait encore l’objet d’un rendu dynamique. Nous avons donc corrigé les rendus dynamiques involontaires, ajouté l’ISR (régénération statique incrémentale) aux routes très fréquentées, rendu davantage de pages compatibles avec la mise en cache et optimisé le comportement coûteux des sitemaps.
Ce travail a aidé, mais il a aussi rendu le problème de fond plus clair. Nous passions de plus en plus de temps à comprendre pourquoi du contenu relativement statique était rendu de façon dynamique, quel comportement du framework en était la cause et quelle fonctionnalité du framework il fallait introduire pour pouvoir à nouveau le mettre en cache.
Pendant ce temps, beaucoup de nos autres applications présentaient le problème inverse. Les paramètres de compte, la console développeur, les applications d’administration et la plupart des interfaces produit n’avaient absolument pas besoin de rendu côté serveur. C’étaient des applications exécutées dans le navigateur qui communiquaient avec des API.
À ce stade, la question n’était plus « Comment réduire notre facture Vercel ? », mais « Que construirions-nous si ces applications n’étaient pas déjà des applications Next.js ? »
Nous avons envisagé Next.js sur Cloudflare
L’option la moins perturbatrice consistait à conserver Next.js et à le déplacer de Vercel vers Cloudflare Workers.
Nous l’avons sérieusement envisagée, car Next.js peut fonctionner sur Cloudflare grâce à des couches d’adaptation. Cela nous aurait permis de préserver une grande partie de la structure de nos applications existantes.
Mais nous ne considérions pas Next.js sur Cloudflare comme l’équivalent de Next.js sur Vercel. Next.js est plus étroitement intégré à l’environnement d’exécution, au système de déploiement, au comportement de mise en cache et aux fonctionnalités de la plateforme Vercel. Sur Cloudflare, un adaptateur doit transposer ces hypothèses dans un environnement d’exécution différent.
Cela peut très bien fonctionner, mais si notre objectif était de simplifier la pile technique, ajouter une couche de compatibilité supplémentaire ne nous semblait pas idéal.
Il y avait aussi une question d’outillage plus générale. Presque tous nos autres projets frontend utilisaient déjà Vite : applications de bureau, extensions de navigateur, SPA et autres projets côté client.
Next.js s’était fortement orienté vers Turbopack, qui s’est beaucoup amélioré. Il ne s’agissait pas d’une critique de ses performances. Pour nous, la principale différence concernait l’écosystème. Vite constituait déjà la base commune de la majeure partie de notre code frontend, avec un écosystème de plugins plus vaste et des configurations plus facilement réutilisables d’un projet à l’autre.
Conserver Next.js sur Cloudflare aurait donc résolu une partie du problème d’hébergement, mais aurait maintenu à la fois le décalage entre frameworks et un écosystème d’outils de compilation distinct.
Nous avons donc pris du recul pour examiner les besoins réels de chaque type de charge de travail.
Nous avons réparti la pile selon les usages
Dès que nous avons cessé de chercher un remplaçant universel à Next.js, l’architecture est devenue beaucoup plus simple.
Nos sites marketing avaient besoin du SSR : ils comportent des pages publiques et localisées, des métadonnées, des URL canoniques et des sitemaps, et reçoivent du trafic issu des moteurs de recherche.
La plupart de nos autres applications Web n’avaient pas besoin du SSR et pouvaient simplement être des applications Vite utilisant TanStack Router.
Nos API n’avaient pas du tout besoin d’un framework React et étaient mieux adaptées à un environnement d’exécution Node.js classique.
La répartition finale ressemblait à ceci :
Applications Web
Vite + TanStack Router
│
└── Cloudflare Workers
Sites marketing
TanStack Start + SSR
│
└── Cloudflare Workers
API
Hono + tRPC
│
└── Railway / conteneurs Node.js
La règle est devenue simple : utiliser le SSR là où il apporte une réelle valeur ajoutée, une simple SPA là où ce n’est pas le cas, et exécuter les services backend dans un environnement adapté au backend.
La plupart des applications Web sont devenues des SPA
Les migrations vers des SPA ont été la partie la plus facile.
L’application Web WebCatalog a migré particulièrement vite : nous avons créé la structure du projet de remplacement avec Vite + TanStack Router, porté l’interface, basculé le déploiement sur Cloudflare Workers et supprimé l’ancienne version Next.js dans la même matinée.
Nous avons repris la même méthode pour l’application Web Lexibird, les paramètres de compte, notre console développeur, l’authentification et les applications d’administration. Entre la création de la première structure de SPA et la suppression de la dernière application Next.js, il s’est écoulé environ 19 jours.
Pour ces applications, le fonctionnement de Cloudflare Workers est volontairement banal. Vite compile des ressources statiques, Cloudflare les sert et les routes de l’application se replient sur index.html. Il n’y a pas d’environnement d’exécution SSR, puisqu’aucun élément ne nécessite de rendu côté serveur.
Le plus difficile a été de préserver les comportements qui s’étaient accumulés autour de ces applications au fil du temps. Une partie de la logique d’authentification a dû être déplacée des routes serveur Next.js vers l’API, et d’anciennes versions de l’application de bureau WebCatalog dépendaient encore de points de terminaison historiques que nous devions maintenir en fonctionnement.
Déplacer l’interface React était facile.
Préserver les contrats l’était moins.
Nous avons essayé Astro, puis l’avons supprimé cinq jours plus tard
Les sites marketing ont été plus difficiles.
Notre premier choix s’est porté sur Astro, qui semblait naturellement convenir à webcatalog.io et lexibird.com : ce sont deux sites publics riches en contenu, et Astro s’intègre bien à Cloudflare.
Nous avons commencé à porter les deux sites, puis nous nous sommes arrêtés cinq jours plus tard.
Il n’y avait pas de défaut rédhibitoire unique. C’étaient plutôt de petits décalages qui s’accumulaient. Certaines routes avaient besoin d’accéder à la base de données lors du prérendu, alors que notre environnement de compilation ne disposait volontairement pas des identifiants de la base de production. Les îlots React rendaient moins naturelle la gestion d’un état partagé à l’échelle de l’application. Nous avons aussi rencontré des différences entre les routes prérendues et celles rendues à l’exécution, ainsi que quelques difficultés liées aux outils dans notre dépôt.
Aucun de ces problèmes n’était insoluble. C’est précisément ce qui rendait la poursuite du projet risquée : nous aurions facilement pu passer encore plusieurs semaines à régler les problèmes les uns après les autres.
Nous avons préféré supprimer cette implémentation et repartir de zéro.
Les agents IA ont changé l’équation économique de cette décision. Une grande partie du portage mécanique n’avait pas mobilisé des semaines de travail d’ingénieurs : le coût irrécupérable était donc plus faible, et il était plus facile de reconnaître que nous préférions une autre architecture.
TanStack Start nous convenait mieux
Nous avons repris la migration des sites marketing à partir du code Next.js d’origine, cette fois avec TanStack Start.
Ce choix s’intégrait bien plus naturellement à notre environnement, puisque nous utilisions déjà TanStack Router dans nos SPA. Le routage, les loaders, les paramètres de recherche et la navigation reposaient ainsi sur des concepts similaires. Le code restait du React et du TypeScript classiques, et le système de compilation était Vite.
Nous avons porté lexibird.com vers TanStack Start en une journée. webcatalog.io a suivi.
L’avantage n’était pas que TanStack Start résolvait tous les problèmes comme par magie, mais qu’il s’inscrivait dans la direction que prenait déjà le reste de notre pile technique.
Dans un monorepo, cela compte. Des outils de compilation communs permettent de réutiliser davantage de plugins et de configurations, réduisent les cas particuliers lors du débogage, limitent les changements de contexte pour les développeurs et laissent moins de conventions propres à chaque projet que les agents de programmation doivent découvrir.
Cloudflare était beaucoup moins cher pour notre trafic
L’architecture n’explique qu’une partie de la baisse de notre facture. La tarification de Cloudflare a joué un rôle majeur, en particulier pour la bande passante.
L’offre payante Cloudflare Workers facture actuellement principalement les requêtes et l’utilisation du processeur, et n’ajoute pas de frais de transfert de données ou de bande passante pour Workers. (developers.cloudflare.com) C’est très important pour les sites Web publics : une requête peut consommer très peu de temps processeur tout en transférant du HTML, du JavaScript, des images, des polices et d’autres ressources.
Le modèle tarifaire de Vercel comporte également des catégories d’utilisation et des quotas liés au réseau, notamment Fast Data Transfer et Fast Origin Transfer. (vercel.com) Pour notre profil de trafic, Cloudflare était nettement plus avantageux sur le plan économique.
Les économies ne sont pas venues uniquement du changement de fournisseur. Nous avons aussi converti de nombreuses applications en versions statiques compilées avec Vite, réduit le SSR inutile, rendu la mise en cache plus explicite et déplacé les charges de travail de nos API vers un environnement d’exécution mieux adapté. Mais la tarification de Cloudflare, notamment pour la bande passante, a largement contribué à la baisse d’environ 80 % que nous avons constatée.
Ce chiffre est propre à notre charge de travail. Il ne signifie pas que toute application passant de Vercel à Cloudflare économisera 80 %.
Les API ont finalement atterri sur Railway
Les API posaient un problème distinct.
Bien qu’il s’agisse de projets Next.js, elles en étaient déjà largement indépendantes. L’API WebCatalog comptait environ 34 000 lignes, mais seuls sept fichiers importaient next/server. tRPC utilisait déjà son adaptateur Fetch, et Inngest prenait en charge Hono.
Nous avons remplacé la couche HTTP de Next.js par Hono. Cette partie s’est révélée étonnamment simple.
La question la plus difficile était de savoir où l’exécuter. Les Workers classiques ne convenaient pas, car les API utilisent Node.js et des dépendances natives comme sharp.
Nous avons d’abord essayé Cloudflare Containers, ce qui nous a permis de quitter Vercel rapidement. Mais après avoir utilisé cette configuration en production, nous avons constaté que son modèle d’exploitation demandait davantage d’ajustements manuels de capacité que nous ne le souhaitions à ce moment-là.
Nous avons donc déplacé les API une nouvelle fois.
Aujourd’hui, elles s’exécutent sur Railway sous forme de services de conteneurs Node.js classiques, à exécution continue. La CI (intégration continue) construit les images Docker et les envoie vers notre registre, tandis que Railway gère la mise à l’échelle horizontale automatique.
Tout n’a pas besoin de s’exécuter en périphérie du réseau.
Quitter Vercel signifiait assumer davantage de responsabilités
La baisse des coûts s’accompagnait d’un compromis.
Vercel et Next.js prenaient en charge de nombreux aspects de l’infrastructure à un niveau plus élevé. Avec TanStack Start et Workers, nous nous sommes plutôt orientés vers une mise en cache HTTP explicite. Une page est rendue, nous renvoyons des en-têtes de cache, puis Cloudflare met la réponse en cache. La mise en cache dans le navigateur et en périphérie du réseau peut suivre des politiques différentes, notamment la possibilité de servir une réponse périmée pendant sa revalidation en périphérie.
Nous appréciions que ces décisions deviennent explicites, mais cela signifie aussi que les erreurs d’infrastructure sont de notre responsabilité.
Nous en avons commis quelques-unes. Une règle de cache en production a accidentellement correspondu à des réponses de fonctions serveur TanStack propres à chaque visiteur, et une autre configuration de cache a mal interagi avec le mécanisme de repli des SPA. Ces bugs nous ont utilement rappelé que l’exactitude d’une migration ne peut pas être entièrement démontrée à partir du dépôt de code. L’infrastructure de production fait elle aussi partie du système.
Les agents IA ont changé notre façon de migrer
La majeure partie du travail de mise en œuvre a été effectuée par des agents de programmation, principalement Claude Fable 5 et Claude Opus 5.
Les migrations entre frameworks se prêtent particulièrement bien au travail des agents, car une grande partie des tâches dispose d’une référence existante claire. Déplacer cette route, préserver cette URL, remplacer cette API de framework, conserver les mêmes métadonnées, lancer la vérification des types, corriger les erreurs et comparer le résultat à l’ancienne implémentation.
Pour webcatalog.io, nous avons tenu un tableau de suivi de la migration, découpé en étapes telles que les fondations, les pages produit, les pages catalogue, la recherche, le blog, les tarifs, les redirections, les sitemaps et la bascule. Au lieu de demander à un agent de « migrer webcatalog.io vers TanStack Start », nous lui avons confié des tâches délimitées, assorties d’exigences explicites à respecter.
L’application existante est devenue la spécification. Le rôle de l’agent était de reproduire son comportement avec la nouvelle architecture, et non de réinventer le produit.
L’effort humain s’est ainsi déplacé de la traduction manuelle du code vers des questions telles que : cette page a-t-elle vraiment besoin du SSR ? Quel comportement est intentionnel ? Qu’est-ce qui peut être mis en cache pour tous les utilisateurs ? Quelles URL doivent rester identiques ? Où l’authentification doit-elle avoir lieu ? À quel moment faut-il cesser d’essayer de faire fonctionner une approche ?
Nous avons tout de même passé les résultats en revue, lancé les compilations et les vérifications de types, et vérifié manuellement les domaines les plus risqués, comme l’authentification, le SEO (référencement naturel), les redirections et la mise en cache.
Le changement important n’était pas que l’IA écrivait du code pour nous. C’était qu’elle rendait l’expérimentation moins coûteuse.
Essayer Astro puis l’abandonner au bout de cinq jours devenait beaucoup plus facile à justifier. Déplacer les API une première fois, puis décider qu’une autre plateforme convenait mieux, était moins pénible. Le travail mécanique coûtait moins cher : nous pouvions donc changer de direction quand l’architecture n’était pas la bonne, au lieu de poursuivre simplement parce que nous y avions déjà trop investi.
Les agents ont rendu la mise en œuvre moins rare.
Cela a rendu l’architecture, la définition des contraintes, la revue et la vérification d’autant plus importantes.
De 80 %, nous sommes passés à environ 90 %
Après la migration, nos coûts d’infrastructure Web avaient baissé d’environ 80 %.
Nous avons ensuite commencé à examiner de plus près quelles requêtes atteignaient réellement les applications.
Une part non négligeable du trafic sur l’Internet public est automatisée : moteurs de recherche, robots d’exploration IA, agents, collecteurs de données, scanners et bots moins bienveillants. Une partie de ce trafic est utile, une autre non.
Les applications se trouvant directement derrière Cloudflare, nous avons renforcé nos règles de sécurité pour rejeter le trafic automatisé hostile en périphérie du réseau, avant qu’il ne déclenche des calculs applicatifs ou des opérations sur la base de données.
Après cela, nos économies totales ont atteint environ 90 % par rapport à l’ancienne configuration.
Cette baisse finale résulte de plusieurs facteurs combinés : les tarifs plus bas de Cloudflare, notamment pour la bande passante ; moins de travail côté serveur ; un environnement d’exécution mieux adapté à nos API ; une mise en cache plus explicite ; et moins de requêtes inutiles atteignant les applications.
Notre situation actuelle
Il ne reste plus d’applications Next.js dans notre dépôt. La plupart de nos applications Web sont désormais des SPA Vite + TanStack Router sur Cloudflare Workers. webcatalog.io et lexibird.com utilisent TanStack Start sur Cloudflare Workers, car ces sites tirent réellement parti du SSR. Nos API utilisent Hono + tRPC sur Railway et s’exécutent dans des conteneurs Node.js classiques. Presque tous nos projets frontend font désormais partie de l’écosystème Vite.
Nos coûts d’infrastructure ont baissé d’environ 80 %, en grande partie grâce à des tarifs Cloudflare nettement plus avantageux pour notre trafic, notamment en matière de bande passante. Le filtrage du trafic automatisé hostile en périphérie du réseau a porté les économies globales à environ 90 %.
Mais le résultat qui compte le plus pour nous est architectural. Les sites marketing bénéficient du SSR, tout ce qui peut être une SPA est une SPA, et les API s’exécutent sur de vrais serveurs lorsque c’est la solution la plus simple.
Au départ, nous cherchions à réduire notre facture Vercel.
À l’arrivée, nous avons compris que nous n’avions pas besoin de l’essentiel de l’architecture pour laquelle nous payions.