Comment fonctionne Centrifugo
Centrifugo se place entre votre application et ses utilisateurs. Votre backend — Laravel, Django, Express, Rails ou autre — ne garde aucune connexion ouverte : il signe un jeton pour chaque utilisateur, puis publie des messages par un simple appel HTTP. Centrifugo, lui, maintient les connexions WebSocket des navigateurs et des applications mobiles, et livre chaque message aux abonnés du canal concerné.
Cette séparation a un avantage concret : votre application reste sans état et facile à déployer, et la partie temps réel tient dans un binaire Go unique qui consomme quelques dizaines de Mo de RAM au repos.
Ce que Centrifugo apporte
- Connexions WebSocket — avec des SDK clients officiels pour JavaScript, Dart/Flutter, Swift, Java/Android, Go et Python.
- API de publication — un POST HTTP avec l'en-tête
X-API-Key, depuis n'importe quel langage. - Jetons JWT — votre backend décide qui se connecte et à quels canaux, Centrifugo vérifie la signature.
- Abonnements côté serveur — la liste des canaux d'un utilisateur voyage dans son jeton.
- Console d'administration — état du nœud, clients connectés et commandes d'API depuis le navigateur.
- Défauts prudents — aucune connexion anonyme, aucune publication depuis le navigateur sans autorisation explicite.
Centrifugo face à un service hébergé
Faites défiler le tableau
| Centrifugo sur VPS | Pusher, Ably | |
|---|---|---|
| Facturation | Prix du VPS, sans limite de messages | Au nombre de connexions et de messages |
| Où passent les données | Sur votre serveur | Chez le fournisseur |
| Protocole client | WebSocket et SDK officiels | WebSocket et SDK propriétaires |
| Authentification | JWT signé par votre backend | Signature propre au service |
| Exploitation | À votre charge | Assurée par le fournisseur |
Prérequis
Un VPS Ubuntu 24.04 avec 1 Go de RAM et 10 Go de disque, Docker et le plugin Compose installés, et un sous-domaine qui pointe vers le VPS, par exemple rt.example.com. Le HTTPS n'est pas optionnel : un site servi en HTTPS ne peut ouvrir que des connexions wss://, et votre clé d'API ne doit jamais circuler en clair.
Installer Centrifugo avec Docker Compose
Générer les secrets
Créez le répertoire de l'application et un fichier
.envqui porte quatre secrets distincts : le mot de passe de la console, la clé qui signe ses sessions, la clé de l'API serveur et la clé HMAC des jetons clients.mkdir -p /opt/centrifugo && cd /opt/centrifugo { echo "ADMIN_PASSWORD=$(openssl rand -hex 12)" echo "SECRET_KEY=$(openssl rand -hex 24)" echo "ENCRYPTION_KEY=$(openssl rand -hex 16)" echo "ENCRYPTION_KEY_HEX64=$(openssl rand -hex 32)" } > .env chmod 600 .envÉcrire le fichier docker-compose.yml
Le port est publié sur la boucle locale uniquement : seul nginx y accède. Remplacez
https://app.example.compar l'adresse de votre site.services: centrifugo: image: centrifugo/centrifugo:v6.10.2 restart: unless-stopped command: ["centrifugo"] ports: - "127.0.0.1:8000:8000" environment: CENTRIFUGO_ADMIN_ENABLED: "true" CENTRIFUGO_ADMIN_PASSWORD: "${ADMIN_PASSWORD}" CENTRIFUGO_ADMIN_SECRET: "${SECRET_KEY}" CENTRIFUGO_HTTP_API_KEY: "${ENCRYPTION_KEY}" CENTRIFUGO_CLIENT_TOKEN_HMAC_SECRET_KEY: "${ENCRYPTION_KEY_HEX64}" CENTRIFUGO_CLIENT_ALLOWED_ORIGINS: "https://app.example.com" CENTRIFUGO_HEALTH_ENABLED: "true" ulimits: nofile: soft: 65535 hard: 65535Démarrer et vérifier
Lancez le conteneur et interrogez le point de santé ; il doit répondre
{}.docker compose up -d curl -fsS http://127.0.0.1:8000/health
Ne publiez jamais le port 8000 sur l'IP publique
Le même port sert les connexions clients, l'API serveur et la console d'administration. Gardez-le sur 127.0.0.1 et passez toujours par le reverse proxy HTTPS. Et ne mettez jamais ENCRYPTION_KEY ni ENCRYPTION_KEY_HEX64 dans du code envoyé au navigateur : la première permet de publier sur n'importe quel canal, la seconde de fabriquer des jetons pour n'importe quel utilisateur.
Placer Centrifugo derrière nginx
Une connexion WebSocket commence par une requête HTTP qui demande un changement de protocole. nginx doit transmettre cette demande, et ne pas couper une connexion qui reste silencieuse quelques minutes. Dans le bloc server de votre sous-domaine, avec un certificat Let's Encrypt :
location / {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 3600s;
}Après nginx -t et un rechargement, https://rt.example.com affiche l'écran de connexion de la console.
Publier depuis votre backend
Publier, c'est envoyer un objet JSON à un canal. N'importe quel langage capable de faire une requête HTTP convient :
curl -X POST https://rt.example.com/api/publish \
-H "X-API-Key: $ENCRYPTION_KEY" \
-d '{"channel": "commandes", "data": {"id": 1042, "statut": "payee"}}'La réponse {"result":{}} confirme que le message est accepté. Sans en-tête X-API-Key, ou avec une mauvaise clé, Centrifugo répond 401.
Signer les jetons de connexion
Un navigateur ne se connecte qu'avec un jeton JWT signé par votre backend avec ENCRYPTION_KEY_HEX64. La revendication sub porte l'identifiant de l'utilisateur, exp la date d'expiration, et channels la liste des canaux auxquels il est abonné d'office. Exemple en Python :
import time, jwt # pip install pyjwt
def centrifugo_token(user_id: str) -> str:
claims = {"sub": user_id, "exp": int(time.time()) + 3600, "channels": ["commandes"]}
return jwt.encode(claims, ENCRYPTION_KEY_HEX64, algorithm="HS256")Exposez cette fonction derrière une route authentifiée de votre application : seul un utilisateur connecté chez vous doit pouvoir obtenir un jeton.
Ce qu'un client a le droit de faire
Les réglages de cette installation sont volontairement fermés. Un navigateur ne peut ni s'abonner de lui-même à un canal, ni y publier : il reçoit uniquement les canaux listés dans la revendication channels de son jeton. C'est votre backend qui décide, à chaque émission de jeton, qui écoute quoi — un client ne voit que ses propres commandes, un agent du support que la file de son équipe.
Pour un chat, la publication passe donc elle aussi par votre backend : le navigateur envoie le message à votre application, qui le valide, l'enregistre en base, puis le publie par l'API. Ce détour coûte quelques millisecondes et vous garde la main sur la modération et l'historique.
Donnez enfin aux jetons une durée courte, une heure par exemple. Plutôt que de passer un jeton figé, laissez le SDK en redemander un à votre application quand il expire :
const client = new Centrifuge('wss://rt.example.com/connection/websocket', {
getToken: async () => {
const res = await fetch('/realtime/token', { credentials: 'include' });
return (await res.json()).token;
},
});La route /realtime/token est celle de votre application ; elle vérifie la session de l'utilisateur avant de signer quoi que ce soit.
Connecter un navigateur
Côté client, le SDK officiel centrifuge (npm) ouvre la connexion et rend les messages :
import { Centrifuge } from 'centrifuge';
const client = new Centrifuge('wss://rt.example.com/connection/websocket', {
token: tokenFromYourBackend,
});
client.on('publication', (ctx) => console.log(ctx.channel, ctx.data));
client.connect();Si la connexion échoue, regardez d'abord les journaux (docker compose logs centrifugo). Le message « request Origin is not authorized » signifie que l'origine de votre site manque dans CENTRIFUGO_CLIENT_ALLOWED_ORIGINS ; « invalid token » désigne un jeton mal signé ou expiré.
La console d'administration
Ouvrez https://rt.example.com et saisissez ADMIN_PASSWORD : la console ne demande pas d'identifiant. Elle affiche la version du nœud, le nombre de clients et d'utilisateurs connectés, et permet d'envoyer des commandes d'API — publier un message de test, lister les canaux actifs — sans écrire de code. C'est l'outil le plus rapide pour vérifier qu'un message part bien avant de chercher le défaut côté navigateur.
Montée en charge et mises à jour
Ce guide utilise le moteur mémoire : un seul nœud, sans dépendance. Il convient à la plupart des applications, et un VPS modeste tient déjà un grand nombre de connexions simultanées, d'où la limite de fichiers ouverts relevée dans le docker-compose.yml. Le jour où un nœud ne suffit plus, Centrifugo sait répartir la charge entre plusieurs nœuds reliés par Redis. Pour mettre à jour, changez le tag de l'image après avoir lu les notes de version, puis relancez docker compose pull && docker compose up -d ; les connexions se rétablissent seules, les SDK clients se reconnectant automatiquement.