HTML via WebSockets en 2026 : quand remplacer votre SPA

Par Diogo Hudson Dias
Engineer in a São Paulo office watching a real-time admin dashboard update on a desktop monitor with servers in the background.

Vous payez la taxe SPA pour des pages qui, pour l'essentiel, remuent des formulaires et des tableaux. Pendant ce temps, votre équipe se débat avec des bugs d'hydratation et des cas limites de réactivité qui n'ont rien à voir avec votre métier. Il existe une voie plus simple, redevenue soudain moderne : HTML via WebSockets — une UI rendue côté serveur, diffée et diffusée au navigateur avec presque pas de JavaScript.

L'idée n'est pas nouvelle. Ce qui a changé, c'est la viabilité en production. Les équilibreurs de charge, les CDN et les WAF gèrent désormais proprement les connexions persistantes. Des frameworks comme Phoenix LiveView, Rails Turbo Streams (Hotwire), Laravel Livewire, htmx et Blazor Server de .NET (adossé à SignalR) ont mûri. Un nouveau fil HN cette semaine — « HTML over WebSockets: real-time SPAs with barely any JavaScript » — illustre pourquoi des équipes y reviennent. Si vous exploitez un produit B2B où « temps réel » signifie notifications, tableaux de bord, curseurs collaboratifs et flux de tokens IA, évaluez-le.

La décision : quand l'HTML-over-the-wire gagne

Utilisez ceci comme cadre go/no-go. Plus vous cochez de cases, plus le cas est solide.

1) Votre UI est surtout en lecture, axée formulaires ou dashboards

  • Consoles CRUD, dashboards d'exploitation, outils back-office, SaaS B2B avec interaction modérée.
  • Indicateurs collaboratifs (présence, curseurs), notifications, ou mises à jour à moins de 10 Hz (ex. rafraîchissements de tableaux, voyants d'état) — pas des jeux canvas à 60 fps.
  • Le SEO compte ? L'HTML côté serveur est du SEO natif. Pas de danse d'hydratation.

2) Votre équipe est majoritairement back-end ou régulée

  • Les ingénieurs back-end peuvent livrer des fonctionnalités UI sans le parcours du combattant React/GraphQL.
  • La sécurité/la conformité préfèrent que le serveur soit la source de vérité ; moins de logique côté client, moins de secrets dans le navigateur.

3) Vous n'avez pas besoin d'un vrai mode offline-first

  • L'HTML-over-the-wire part du principe que l'on est en ligne. Les modes hors-ligne au-delà de simples retries optimistes de formulaires sont pénibles. Si l'offline est central, restez en SPA/native.

4) Vous avez des besoins temps réel au-delà du streaming unidirectionnel

  • Si vous n'avez besoin que de serveur-vers-client (ex. sortie de tokens IA), SSE est plus simple. Si vous avez aussi besoin de pings client, de formulaires live ou de présence, WebSockets unifie les protocoles.

Si cela ressemble à votre produit, vous pouvez probablement supprimer 70–90 % de votre code front sur ces surfaces. Dans nos pods de livraison, les fonctionnalités B2B à forte composante CRUD ont avancé 25–40 % plus vite après une migration d'une coque React/GraphQL/SPA vers de l'HTML piloté par le serveur avec un client fin. Moins de dépendances, moins de bugs d'état, moins d'artefacts de build qui cassent.

À quoi ressemble vraiment « HTML over WebSockets »

Vous conservez le rendu côté serveur, mais celui-ci envoie des fragments HTML incrémentaux sur un socket persistant à chaque changement d'état. Le navigateur applique des diffs au DOM vivant. Un minimum de code client, généralement une librairie de morph/patch :

  • Phoenix LiveView : le serveur suit un processus par vue client ; il pousse des diffs efficaces via WebSockets ; le client patche le DOM.
  • Rails Hotwire/Turbo Streams : le serveur émet des fragments HTML et des directives ; le navigateur mue (morph) le DOM par ID ou insère via des streams.
  • Laravel Livewire : PHP rend des composants côté serveur ; les diffs et événements circulent via AJAX/WebSockets.
  • htmx : HTML piloté par l'hypermédia avec des attributs ; ajoutez un endpoint WebSocket ou SSE ; htmx échange des snippets de DOM.
  • Blazor Server : les composants sont rendus côté serveur ; SignalR synchronise les diffs de composants vers le client.

En pratique : vous écrivez des templates/composants côté serveur ; vous émettez des événements quand le modèle change ; le framework ventile les plus petits patchs DOM possibles vers les clients connectés. Vous évitez de dupliquer validation et logique métier côté client. Un seul modèle mental.

Architecture et montée en charge sans mauvaises surprises

Budgets de connexion

Les sockets persistants ne sont pas gratuits. Planifiez avec des budgets prudents et testables :

  • Mémoire par connexion : 2–10 KB côté app plus la surcharge noyau/TLS. Les valeurs réelles dépendent du framework et de l'état par vue. Phoenix LiveView est réputé frugal ; des états de composants lourds côté serveur ne le sont pas.
  • Concurrence par noeud : 20k–100k connexions WebSocket par instance milieu de gamme (8–16 vCPU, 16–64 GB RAM) est réaliste avec Elixir/Go/Java. Node/.NET peuvent aussi bien performer avec du tuning. Ne devinez pas — faites des tests de charge.
  • Timeouts d'inactivité : définissez 5–20 minutes sur le LB et l'app pour éviter les sessions fantômes. AWS ALB/NLB, GCP HTTPS LB, Cloudflare et Fastly supportent tous WebSockets ; augmentez les valeurs par défaut (souvent 60 s) pour correspondre à votre UX.

Mise à l'échelle stateless avec une couche de canaux

Ne fixez pas tout l'état applicatif à un seul processus. Utilisez une couche channel/pub-sub pour diffuser (fan-out) les mises à jour et survivre aux bascules :

  • Elixir : Phoenix.PubSub (Redis/NATS en option), Presence pour le suivi.
  • Rails/Laravel : pub/sub Redis ; diffusion avec ActionCable/AnyCable (Rails) ou Soketi/service compatible Pusher (Laravel/Node).
  • .NET : Azure SignalR Service ou backplane Redis.

Avec un fan-out via Redis/NATS, vous pouvez faire tourner plusieurs réplicas d'app derrière un LB L7 classique, sans sticky sessions. Si votre framework stocke un état par connexion côté serveur (ex. LiveView), utilisez des registres distribués et rendez les reconnexions peu coûteuses.

Backpressure et coalescence des mises à jour

  • Mettez en file, par connexion, les messages sortants et supprimez/fusionnez si la file sature. Mieux vaut livrer l'état le plus récent que tous les états intermédiaires.
  • Regroupez les patchs DOM à des intervalles de 16–50 ms en cas de burst, pour réduire les tempêtes de patchs et les reflows en rafale dans le navigateur.
  • Limitez les sources haute fréquence (ex. métriques à 10 Hz) ; sous-échantillonnez à 1–2 Hz par widget visible sauf demande explicite d'un utilisateur pour des cadences supérieures.

Authentification et sécurité

  • Authentifiez le handshake WebSocket avec un jeton signé, de courte durée, dérivé de la session utilisateur. Renouvelez-le toutes les quelques minutes. Revalidez à la reprise.
  • La CSRF compte toujours : validez l'origine et incluez des jetons CSRF sur les événements client-vers-serveur qui modifient l'état.
  • Validation d'entrée : traitez les événements socket comme des POST HTTP. Même schéma, mêmes limites de débit, même piste d'audit.
  • WAF : autorisez le chemin d'upgrade WebSocket et fixez des limites raisonnables de taille de trame/message (ex. 64–256 KB). Journalisez les rejets.

Observabilité réellement utile

  • Jauge des sockets connectés et distribution par noeud.
  • Taux de reconnexion par minute et causes (timeout d'inactivité vs réseau vs échec d'auth).
  • Métriques de patch : taille moyenne et p95 des patchs, patchs/sec, RTT client p50/p95.
  • Temps de rendu serveur p50/p95 par composant/template.
  • Compteurs de backpressure : mises à jour supprimées/fusionnées.

Alertez sur les pics de reconnexions, la hausse de la taille p95 des patchs ou les régressions de temps de rendu — ces signaux corrèlent directement avec la perception de saccades.

Choix de protocole : WebSockets vs SSE vs « futures »

  • Server-Sent Events (SSE) : unidirectionnel, text/event-stream sur HTTP/2. Idéal pour la sortie de tokens IA ou des notifications simples. Peu coûteux et ultra simple, mais pas d'événements côté client. Les limites de connexions par origine du navigateur peuvent gêner les power users en multi-onglets.
  • WebSockets : tunnel TCP full-duplex, longue durée. Meilleur pour les entrées collaboratives, la présence, les formulaires live et un temps réel unifié. Un peu plus d'ops, mais désormais courant dans les CDN/LB.
  • WebTransport/QUIC : prometteur, mais pas encore ubiquitaire dans les réseaux/WAF d'entreprise. Aujourd'hui, vous passerez plus de temps à lutter contre les middleboxes qu'à livrer des fonctionnalités.

Règle pratique : si vous avez besoin d'événements client-vers-serveur au-delà de fetch/POST triviaux, choisissez WebSockets et gardez un modèle mental cohérent entre les fonctionnalités.

Performance : ce que les utilisateurs ressentent vraiment

La plupart des UX « temps réel » en entreprise ne visent pas des frames de 16,7 ms ; il s'agit de rendre la bonne chose en ~200 ms et de ne jamais perdre les éditions utilisateur. En pratique :

  • Un diff rendu côté serveur de moins de 1 KB + 50–100 ms de rendu + 50–100 ms de RTT réseau = 100–250 ms de bout en bout. Cela paraît instantané pour des tableaux, formulaires et compteurs.
  • Le navigateur travaille moins : pas de gros VDOM côté client, moins d'incohérences d'hydratation, moins de mégaoctets de JS à livrer/cacher/invalider.
  • Vous payez ce travail côté serveur, où le code est plus simple à tester, profiler et sécuriser. Les CPU coûtent moins cher que les ingénieurs.

Nous avons vu des dashboards d'administration passer de bundles JS de 1–3 MB à moins de 200 KB, supprimer entièrement le routage SPA et réduire le TTI de >50 %. Votre kilométrage variera, mais la tendance est constante.

Coûts et arbitrages (sans lunettes roses)

Ce que vous économiserez

  • Complexité front-end : 70–90 % de JS applicatif en moins sur les surfaces éligibles. Moins de ruptures de build, arbre de dépendances réduit, surface d'attaque plus petite.
  • Vélocité : dans nos pods mixtes nearshore, les fonctionnalités CRUD ont avancé 25–40 % plus vite après migration ; moins de passages de relais, une seule voie de code pour validation et effets de bord.
  • Débogabilité : une seule stacktrace. Les logs serveur se corrèlent directement à l'état UI.

Ce que vous paierez

  • Empreinte de connexions persistantes : vous devez planifier la capacité des WebSockets. Les fuites mémoire vous coûtent désormais de la RAM par connexion.
  • Couplage au framework : LiveView/Turbo/Livewire/Blazor encodent des sémantiques de protocole. Vous êtes « opinionated by default ». Migrer plus tard n'est pas trivial.
  • Tolérance offline/latence : réseaux mobiles à forte latence et mode avion sont peu amicaux. Il faudra des retries soignés et une réconciliation d'état — ou accepter des parcours en ligne uniquement.
  • La QA change de forme : vous écrirez plus de tests d'intégration qui asserteront des patchs DOM ou des parcours de bout en bout plutôt que des tests unitaires d'une machine d'état client.

Si vous exploitez une suite de design à forte interaction, une grille de données avec édition locale complexe, ou des canvases à 60 fps, restez en SPA/native là où le client doit faire autorité. Pour tout le reste, l'UI pilotée par le serveur est compétitive ou supérieure en 2026.

Choisir une stack (shortlist assumée)

  • Phoenix LiveView (Elixir) : si vous pouvez tourner sur la BEAM, faites-le. Vous obtenez des WebSockets de première classe, des processus bon marché, Presence, et un protocole de diff mature. Des rapports de prod à 100k+ sockets par VM sont normaux avec un état raisonnable. Excellent pour les apps collaboratives.
  • Rails + Hotwire (Turbo Streams) : la voie la plus fluide pour les équipes Rails. Super DX, batteries incluses. Couplez avec Redis pour la diffusion. AnyCable améliore les perfs à l'échelle.
  • Laravel Livewire (+ Alpine) : les équipes PHP vont vite ici. Utilisez Soketi ou un service managé compatible Pusher pour le fan-out. Gardez l'état des composants léger.
  • htmx (+ votre serveur) : agnostique langage, très incrémental. Commencez par remplacer un seul widget par SSE/WebSockets. Parfait pour des migrations chirurgicales dans des stacks legacy.
  • .NET Blazor Server : orienté entreprise et bien supporté. Attention à la mémoire par connexion ; profilez de près. Vous bénéficiez de SignalR et d'un outillage solide.

Ne courez pas après des frameworks exotiques pour gagner 20 ms. Choisissez celui que votre équipe peut déboguer à 2 h du matin.

Plan de migration : construire large, livrer ciblé

  1. Identifiez une surface à forte fréquence de changement : un dashboard interne, une console d'exploitation, ou une page client à dominante lecture avec des mises à jour fréquentes.
  2. Instrumentez-la dès aujourd'hui : mesurez p50/p95 TTI, la taille du bundle JS, le taux d'erreur, et le temps pour livrer de petits changements UI (ex. ajouter une colonne, un filtre).
  3. Reconstruisez uniquement cette surface en HTML-over-the-wire (htmx ou l'UI pilotée par le serveur de votre framework). Laissez le reste de votre SPA intact.
  4. Faites tourner les deux 2–4 semaines sous feature flag. Comparez en A/B : vélocité de livraison, taux d'erreur, coût infra, et latence perçue (RUM).
  5. Généralisez le pattern aux surfaces similaires. Évitez de « replatformer » partout. D'abord dashboards et formulaires ; conservez les éditeurs complexes en SPA.

Checklist opérationnelle (copier/coller)

  • Load balancer : augmentez les timeouts à 300–1200 s pour les endpoints /ws. Confirmez le support WebSocket sur le chemin CDN/WAF.
  • Autoscaling : scalez sur sockets connectés, CPU et profondeur de file — pas seulement sur le RPS HTTP.
  • Couche de canaux : Redis/NATS pour le fan-out. Surveillez la latence du pub/sub.
  • Sécurité : jetons d'auth de courte durée, vérification d'origine, CSRF sur événements mutateurs, limites de taille de message.
  • Backpressure : files par connexion avec coalescence et politiques d'abandon. Ne bloquez jamais la boucle d'événements sur un client lent.
  • Métriques : nombre de sockets, reconnexions/min, taille p95 des patchs, rendu p95, RTT p95, abandons par coalescence.
  • Chaos : tuez un noeud pendant une démo. Votre comportement de reconnexion est votre UX.

Et les flux de tokens IA ?

La plupart des équipes streament la sortie du modèle vers le navigateur sous forme de tokens texte. Si c'est votre seul besoin temps réel, SSE reste la voie la plus simple. Mais beaucoup de produits veulent aussi des entrées live (appels d'outils, streaming de fonctions, suggestions de code avec contexte de frappe) et une présence partagée. Un WebSocket par session vous permet de multiplexer les deux sens sous une seule politique de backpressure et une seule histoire d'auth. Vous pouvez toujours véhiculer des flux de tokens sous forme d'événements délimités par ligne dans une trame socket et les rendre en snippets HTML côté serveur.

Bonus : vous gardez les prompts du modèle, la rédaction et le formatage côté serveur. Pas de tokens de prompt ni de contexte sensible dans le client. Cela compte pour les acheteurs entreprise.

Chiffres pour le DAF

  • Infra : attendez-vous à une légère hausse de CPU/RAM serveur pour le rendu + les connexions. Pour une app de dashboard B2B typique avec 5–20k utilisateurs concurrents, les coûts infra additionnels sont souvent de quelques milliers par mois — bien moins qu'un poste senior front-end.
  • Vélocité : les équipes rapportent 25–40 % de livraison plus rapide sur les fonctionnalités CRUD où la logique métier passe côté serveur. Nos pods mixtes US–Brazil ont vu des cycles de deux semaines tomber à 1,2–1,5 semaine pour un périmètre similaire une fois l'échafaudage SPA écarté.
  • Fiabilité : moins de dépendances côté client signifie moins de correctifs d'urgence pour des ruptures de packages. Votre risque se déplace vers la capacité serveur, que vous savez déjà gérer.

Ce sont des chiffres de cadrage, pas des garanties. Faites un pilote sur deux sprints et mesurez vos propres bases.

Conclusion

L'HTML via WebSockets n'est pas un coup de nostalgie. En 2026, c'est une manière prête pour la production de livrer des apps temps réel avec moins de pièces mobiles. Si votre produit, ce sont des formulaires, des tableaux et des dashboards avec une interactivité modérée, vous n'avez pas besoin d'une méga-SPA. Gardez le cerveau sur le serveur. Streamez l'UI. Mesurez les résultats. Étendez là où ça gagne.

Points clés

  • Utilisez l'HTML-over-the-wire lorsque votre app est riche en formulaires/tableaux, sensible au SEO, et seulement modérément interactive.
  • Choisissez WebSockets plutôt que SSE quand vous avez besoin d'événements bidirectionnels ; utilisez SSE pour des flux sortants uniquement.
  • Planifiez la capacité : 20k–100k sockets par noeud sont atteignables ; budgétez quelques KB par connexion plus l'état du framework.
  • Adoptez de manière incrémentale : commencez par un dashboard ou une page admin ; mesurez en A/B avant d'étendre.
  • Instrumentez les bons signaux : sockets, reconnexions, taille des patchs, rendu p95, RTT p95, abandons dûs à la backpressure.
  • Attendez-vous à moins de bugs front-end et à 25–40 % de livraison plus rapide sur les fonctionnalités éligibles ; accordez plus d'attention à la capacité serveur.

Références : htmx et WebSockets, Phoenix LiveView, Turbo Streams, Blazor Server

Ready to scale your engineering team?

Tell us about your project and we'll get back to you within 24 hours.

Start a conversation