---
term: 'Présence (Statut en Ligne)'
seoTitle: 'Systèmes de présence : statut en ligne, heartbeats, vu pour la dernière fois'
headline: "Qu'est-ce que la présence (statut en ligne) ?"
slug: presence-statut-en-ligne
category: api-realtime
shortDefinition: 'La présence est un signal temps réel indiquant si un utilisateur est en ligne, absent ou hors ligne, maintenu à jour par les connexions et les heartbeats.'
relatedTerms:
  - real-time-live-queries
  - websockets-real-time-sync
  - pub-sub-pattern
  - sse-vs-websockets-vs-polling
contrastsWith:
  - push-notifications-apns-fcm
aboutTerms:
  - 'Online Status'
  - 'Last Seen'
  - 'Heartbeat'
  - 'Typing Indicators'
faq:
  - question: "Qu'est-ce qu'un système de présence ?"
    answer: "Un service qui suit, pour chaque utilisateur, s'il est en ligne, absent ou hors ligne, et qui diffuse les changements en temps réel aux utilisateurs autorisés à les voir. Sous le capot : des connexions persistantes ou des heartbeats périodiques qui écrivent dans un stockage en mémoire rapide, et des changements de statut distribués en fan-out via pub/sub."
  - question: 'Comment fonctionne « vu pour la dernière fois » ?'
    answer: "Le serveur enregistre un timestamp à chaque heartbeat et à chaque déconnexion. Tant que vous êtes en ligne, c'est le statut en direct qui est servi ; dès que vous passez hors ligne, c'est le dernier timestamp enregistré qui répond. C'est la seule pièce durable d'un système par ailleurs éphémère — le statut expire, le « vu pour la dernière fois » persiste."
  - question: 'Quel intervalle de heartbeat utiliser pour la présence ?'
    answer: "Trente secondes est la valeur par défaut courante en production, avec un délai de passage hors ligne fixé à deux ou trois fois l'intervalle (60 à 90 secondes). Des intervalles plus courts réduisent le délai de détection, mais multiplient la charge d'écriture proportionnellement à votre population en ligne ; l'intervalle doit aussi rester inférieur aux délais d'inactivité de l'infrastructure, faute de quoi les proxys coupent d'abord les connexions silencieuses."
  - question: 'Détection par connexion ou par heartbeat : laquelle est la meilleure ?'
    answer: "Les deux, pour des défaillances différentes. Les événements de connexion (ouverture/fermeture) donnent des transitions instantanées, mais ratent les morts silencieuses — un appareil qui perd son alimentation n'envoie aucune trame de fermeture et laisse une connexion zombie. L'expiration des heartbeats borne cette obsolescence, au prix d'un délai de détection. Les systèmes de production utilisent les événements de connexion comme voie rapide et le TTL des heartbeats comme source de vérité."
  - question: "Comment empêcher le statut en ligne de clignoter ?"
    answer: "Appliquez un debounce à la transition vers hors ligne : à la déconnexion, lancez un minuteur de grâce — souvent 30 à 60 secondes — et annulez-le si l'utilisateur se reconnecte, en ne publiant « hors ligne » que lorsque le minuteur expire. Les utilisateurs dans les ascenseurs et les tunnels ferroviaires se reconnectent sans cesse ; sans période de grâce, chaque liste de contacts où ils apparaissent clignote avec eux."
  - question: "Comment la présence passe-t-elle à l'échelle de millions d'utilisateurs ?"
    answer: "En respectant l'arithmétique du fan-out : chaque changement de statut doit atteindre chaque observateur, donc N contacts multipliés par M transitions explosent vite. Les leviers : un stockage en mémoire avec TTL pour le statut, l'abonnement aux seuls utilisateurs visibles (la liste de conversations ouverte, pas tout le carnet d'adresses), la récupération à la demande du « vu pour la dernière fois » au lieu de le pousser, et le plafonnement de la diffusion des statuts dans les grands groupes."
  - question: 'Comment fonctionne la présence multi-appareil ?'
    answer: "Suivez une connexion ou un heartbeat par appareil, puis fusionnez : l'utilisateur est en ligne tant qu'au moins un appareil l'est, et hors ligne seulement quand le dernier devient silencieux. La politique de fusion habituelle est « le plus disponible l'emporte » — en ligne sur le téléphone l'emporte sur inactif sur l'ordinateur — et le « vu pour la dernière fois » provient de l'appareil le plus récent."
  - question: 'Les utilisateurs peuvent-ils masquer leur statut en ligne ?'
    answer: "Ils devraient pouvoir le faire — la visibilité est une feature produit, pas un ajout de dernière minute : des règles comme tout le monde, contacts uniquement ou personne, généralement avec réciprocité (masquez le vôtre et vous ne voyez plus celui des autres). Les données de présence sont des données comportementales ; définissez qui peut observer qui dans le modèle de permissions, pas dans l'interface."
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'RFC 6121 — XMPP: Instant Messaging and Presence'
    url: 'https://datatracker.ietf.org/doc/html/rfc6121'
  - name: 'RFC 6455 — The WebSocket Protocol (ping/pong, close codes)'
    url: 'https://datatracker.ietf.org/doc/html/rfc6455'
  - name: 'Page Visibility API — MDN Web Docs'
    url: 'https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API'
  - name: 'Beacon API — MDN Web Docs'
    url: 'https://developer.mozilla.org/en-US/docs/Web/API/Beacon_API'
  - name: 'Presence information — Wikipedia'
    url: 'https://en.wikipedia.org/wiki/Presence_information'
cta:
  title: "Des pastilles vertes sans l'infrastructure"
  text: "Construisez la présence sur Back4app avec ce que la plateforme exécute déjà : une classe Presence, des sauvegardes de heartbeat, un abonnement Live Query pour votre liste de contacts et un balayage planifié pour les expirations — permissions comprises."
  linkText: 'Commencez gratuitement'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-09-16'
translationKey: presence-online-status
---

**La présence est un signal temps réel indiquant si un utilisateur est en ligne, absent ou hors ligne, maintenu à jour par les connexions et les heartbeats.** La pastille verte est une interface faussement simple posée sur un problème distribué véritablement difficile : le serveur doit *déduire* l'absence — les appareils annoncent rarement leur propre mort — puis diffuser chaque déduction à tous ceux qui observent, à l'échelle de listes de contacts entières. Standardisé bien avant les messageries modernes, dans les [presence stanzas](https://datatracker.ietf.org/doc/html/rfc6121) de XMPP, le pattern n'a pas changé sur le fond depuis.

## Points clés

| Question | Réponse |
| --- | --- |
| Les états | en ligne · absent/inactif · hors ligne — plus les statuts forcés par l'utilisateur (ne pas déranger) |
| Le mécanisme | Écritures de heartbeat + expiration par TTL ; événements de connexion comme voie rapide |
| Les chiffres | heartbeat d'environ 30 s · passage hors ligne à 2–3× l'intervalle · 30–60 s de grâce contre les oscillations |
| Le complément | Vu pour la dernière fois — le timestamp durable derrière la pastille éphémère |
| La partie difficile | Le fan-out : N observateurs × M changements de statut, multiplié par votre succès |

## Le contrat de heartbeat

Tout le pattern tient en un seul échange :

```text
client       toutes les 30 s :  save { status: "online", lastActiveAt: now }
serveur      toutes les 60 s :  balayage — lastActiveAt plus ancien que 90 s ?  → status: "offline"
                                (TTL = 2–3 × intervalle du heartbeat)
observateur  status = "online"   → pastille verte
             status = "offline"  → "vu pour la dernière fois à 12:41"  ← lastActiveAt, la partie durable
```

Le même contrat en code SDK — heartbeat en sortie, abonnement en entrée :

**JavaScript:**

```javascript
// JavaScript / Node.js — Back4app JS SDK
// Presence: heartbeat your own status, subscribe to your contacts'
const heartbeat = () =>
  myPresence.save({ status: 'online', lastActiveAt: new Date() });
await heartbeat();
setInterval(heartbeat, 30_000); // server sweeps offline at 2–3× this

const contacts = new Parse.Query('Presence');
contacts.containedIn('user', myContactIds);
const sub = await contacts.subscribe();
sub.on('update', (p) => setStatus(p.get('user'), p.get('status')));
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// Presence: heartbeat your own status, subscribe to your contacts'
Future<void> heartbeat() async {
  myPresence
    ..set('status', 'online')
    ..set('lastActiveAt', DateTime.now());
  await myPresence.save();
}
Timer.periodic(const Duration(seconds: 30), (_) => heartbeat());

final contacts = QueryBuilder<ParseObject>(ParseObject('Presence'))
  ..whereContainedIn('user', myContactIds);
final sub = await LiveQuery().client.subscribe(contacts);
sub.on(LiveQueryEvent.update, (p) => setStatus(p.get('user'), p.get('status')));
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// Presence: heartbeat your own status, subscribe to your contacts'
func heartbeat() async throws {
    myPresence.status = "online"
    myPresence.lastActiveAt = Date()
    _ = try await myPresence.save()
}
// Fire every 30 s — the server sweeps offline at 2–3× this

let contacts = Presence.query(containedIn(key: "user", array: myContactIds))
let sub = try await contacts.subscribe()
sub.handleEvent { _, e in
    if case .updated(let p) = e { setStatus(p.user, p.status) }
}
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// Presence: heartbeat your own status, subscribe to your contacts'
fun heartbeat() {
    myPresence.put("status", "online")
    myPresence.put("lastActiveAt", Date())
    myPresence.saveInBackground()
}
timer.scheduleAtFixedRate(0, 30_000) { heartbeat() } // sweep at 2–3× this

val contacts = ParseQuery.getQuery<ParseObject>("Presence")
contacts.whereContainedIn("user", myContactIds)
val sub = ParseLiveQueryClient.Factory.getClient().subscribe(contacts)
sub.handleEvent(SubscriptionHandling.Event.UPDATE) { _, p ->
    setStatus(p.getString("user"), p.getString("status"))
}
```

## Événements de connexion vs. TTL de heartbeat

Les deux modèles de détection, comparés honnêtement — aucune page bien classée ne le fait en un seul tableau :

| | Cycle de vie de la connexion | Heartbeat + TTL |
| --- | --- | --- |
| Signal en ligne | Socket ouvert | Heartbeat récent dans la fenêtre |
| Signal hors ligne | Événement de fermeture / trame de fermeture | Expiration du TTL — aucun heartbeat pendant 2–3 intervalles |
| Vitesse de détection | Instantanée sur une fermeture propre | Délai borné (jusqu'au timeout) |
| Défaillances silencieuses | **Ratées** — une coupure d'alimentation n'envoie aucune trame de fermeture | Détectées — le silence est le signal |
| État côté serveur | Suivi par connexion | Écritures sans état dans un stockage à TTL |
| Mode de défaillance | Utilisateurs « en ligne » zombies | Brefs faux « hors ligne » en cas de heartbeats manqués |

Le [protocole WebSocket](https://datatracker.ietf.org/doc/html/rfc6455) enseigne lui-même la leçon : il embarque des trames de contrôle ping/pong précisément parce que TCP ne révèle pas la disparition d'un pair, et sa taxonomie de codes de fermeture réserve le 1006 à la « fermeture anormale — aucune trame de fermeture reçue », le cas zombie. La présence en production superpose donc les deux modèles : les événements de connexion pour les transitions instantanées, l'expiration des heartbeats comme vérité qui rattrape ce que les événements laissent passer.

## La machine à états

```mermaid
flowchart LR
  accTitle: Machine à états de la présence avec période de grâce
  accDescr: Un utilisateur passe de hors ligne à en ligne à la connexion ou au heartbeat, d'en ligne à absent quand l'app passe en arrière-plan ou que la saisie devient inactive, et vers hors ligne via une période de grâce qui démarre à la déconnexion ou sur des heartbeats manqués et qui est annulée si l'utilisateur se reconnecte avant son expiration.
  OFF["hors ligne<br/>(sert le vu pour la dernière fois)"] -->|"connexion / heartbeat"| ON["en ligne"]
  ON -->|"onglet masqué · saisie inactive"| AW["absent"]
  AW -->|"reprise d'activité"| ON
  ON -->|"déconnexion ou heartbeats manqués"| GR["période de grâce<br/>30–60 s"]
  AW -->|"déconnexion ou heartbeats manqués"| GR
  GR -->|"reconnexion à temps"| ON
  GR -->|"minuteur expiré → publication"| OFF
```

Deux raffinements distinguent les implémentations soignées. **L'absence est détectée côté client :** le serveur ne voit pas un onglet passé en arrière-plan, mais le navigateur si — la [Page Visibility API](https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API) signale les onglets masqués, des listeners de saisie signalent l'inactivité, et un signal hors ligne best-effort à la fermeture de l'onglet passe par la [Beacon API](https://developer.mozilla.org/en-US/docs/Web/API/Beacon_API). **Le hors ligne passe par un debounce :** l'état de grâce existe parce que les connexions mobiles oscillent — publiez « hors ligne » à chaque tunnel et à chaque ascenseur, et la liste de contacts de chaque observateur clignote par solidarité. Retardez l'annonce, annulez-la à la reconnexion, et l'oscillation s'apaise. Une dernière distinction à emprunter au protocole qui a formalisé la présence : la présence *calculée* (en ligne, d'après la connexion) face au statut *défini par l'utilisateur* (ne pas déranger) — ce dernier l'emporte toujours lors de la fusion.

## Éphémère par conception

La présence est un cache de la réalité, pas un enregistrement : un statut qui arrive en retard est pire que pas de statut du tout — le « en ligne » d'hier est un mensonge aujourd'hui. Ce principe guide les choix d'implémentation : les statuts vivent dans des stockages rapides avec TTL plutôt que dans des tables durables, ne sont jamais mis en file d'attente pour une livraison différée, et sont les premières données abandonnées en cas de backpressure. Seul `lastActiveAt` mérite d'être persisté. Les **indicateurs de saisie** poussent le principe à l'extrême — une micro-présence limitée à une conversation, qui expire en quelques secondes, avec debounce côté émetteur, et abandonnée plutôt que réessayée ; si « Ada est en train d'écrire… » ne peut pas arriver maintenant, cela ne doit jamais arriver.

## Le problème du fan-out

Le mur de scalabilité de la présence, c'est la multiplication, pas le stockage. Un exemple chiffré : 100 000 utilisateurs simultanés, chacun visible par 50 contacts, chacun changeant d'état seulement 20 fois par heure — soit 100 000 × 50 × 20 = **100 millions d'événements de présence par heure** à livrer, pour une feature qui stocke une petite ligne par utilisateur. Les leviers, dans l'ordre où les actionner : **abonnez-vous étroitement** — observez les utilisateurs à l'écran (la liste de conversations ouverte), pas tout le graphe de contacts ; **tirez au lieu de pousser pour la longue traîne** — récupérez le « vu pour la dernière fois » à l'ouverture d'un profil au lieu de le diffuser en continu ; **plafonnez la diffusion dans les groupes** — au-delà de quelques centaines de membres, affichez la présence lors des interactions, pas pour toute la liste des membres ; et **regroupez les transitions** pour qu'un utilisateur qui oscille coûte un seul événement après debounce, pas trente. Le fan-out lui-même s'appuie sur la mécanique standard du [pub/sub](/glossary/fr/pattern-pub-sub/) — la présence en est le cas limite at-most-once, où abandonner un événement périmé est une feature.

## Multi-appareil et confidentialité

Un utilisateur, trois appareils, une pastille — la présence par *utilisateur* est une fusion de la présence par *appareil*. Suivez le heartbeat de chaque appareil séparément ; l'utilisateur est en ligne tant qu'au moins un appareil l'est, et la politique de fusion est « le plus disponible l'emporte » : en ligne sur le téléphone l'emporte sur inactif sur l'ordinateur, et le mode ne pas déranger de n'importe quel appareil prime sur le reste. Le « vu pour la dernière fois » reflète l'appareil le plus récent. La confidentialité est l'autre moitié de la conception, celle que les blogs d'ingénierie passent sous silence alors que c'est ce que les utilisateurs demandent le plus : règles de visibilité (tout le monde / contacts / personne), réciprocité (masquez le vôtre, perdez de vue celui des autres) et « vu pour la dernière fois » approximatif (« récemment » plutôt qu'une heure précise). Appliquez ces règles dans les permissions de la couche de données — la présence est une donnée comportementale, et un « en ligne à 3 h du matin » qui fuit est une véritable divulgation.

## Cas d'usage courants

- **Chat et messagerie** — la pastille verte canonique, le « vu pour la dernière fois » et les indicateurs de saisie.
- **Outils collaboratifs** — qui est dans le document, présence des curseurs, barres latérales « actif maintenant ».
- **Support et marketplaces** — routage selon la disponibilité des agents, signaux de confiance « le vendeur est en ligne ».
- **Multijoueur et social** — lobbies, listes d'amis, raccourcis « rejoindre un ami ».
- **Outils pour les équipes** — états de disponibilité qui alimentent le routage et les dashboards de statut.

## Comment construire la présence ? Matrice de décision

| Fonctionnalité | Mécanisme |
| --- | --- |
| Pastille verte sur les contacts | Heartbeat + stockage à TTL, abonnement live query sur les utilisateurs visibles |
| Transitions instantanées dans un chat ouvert | Événements de connexion comme voie rapide sur le socket |
| Vu pour la dernière fois | Timestamp à chaque heartbeat ; récupération à la demande |
| Détection de l'absence | Côté client : listeners de visibilité et d'inactivité, remontés dans le heartbeat |
| Indicateur de saisie | Micro-événements par conversation, TTL de quelques secondes, jamais persistés |
| Présence dans un groupe de 5 000 membres | Pas de diffusion — affichage lors des interactions, liste des membres plafonnée |

## Limites et trade-offs

- **La présence est probabiliste.** Entre deux heartbeats, la pastille est une supposition ; les systèmes honnêtes assument une obsolescence bornée plutôt que de prétendre à une vérité instantanée.
- **La fraîcheur se paie en écritures.** Diviser par deux l'intervalle de heartbeat double la charge d'écriture sur l'ensemble des utilisateurs en ligne — la vitesse de détection s'achète en débit.
- **Le fan-out croît avec le succès.** La feature ne coûte rien à 1 000 utilisateurs et devient un projet d'architecture à 10 millions ; concevez la portée des abonnements avant que la croissance ne vous y oblige.
- **Les oscillations sont inévitables.** Les réseaux mobiles garantissent des reconnexions en rafale ; sans debounce, la présence amplifie le bruit réseau en bruit d'interface.
- **Elle se prête naturellement à la surveillance.** Les habitudes de connexion révèlent le sommeil, le travail et les routines ; contrôles de visibilité et application au niveau des permissions sont des exigences, pas des améliorations.

## Présence sur Back4app

Back4app est une plateforme open-source de Backend as a Service (BaaS) qui combine une base de données gérée, des API REST et GraphQL générées automatiquement, l'authentification, le stockage de fichiers et des fonctions serverless avec Cloud Code. Les briques décrites ci-dessus s'y transposent une à une : une classe `Presence` contient `status` et `lastActiveAt` ; le heartbeat est une sauvegarde périodique (les onglets de code) ; les observateurs maintiennent un abonnement [Live Query](/glossary/fr/live-queries-temps-reel/) sur les contacts affichés à l'écran et reçoivent les mises à jour poussées via la flotte [WebSocket](/glossary/fr/websockets/) gérée de la plateforme ; un job Cloud Code planifié assure le balayage TTL, en basculant les lignes périmées à hors ligne et en déclenchant le debounce ; et les permissions au niveau de la classe ainsi que les ACL mettent en œuvre les règles de visibilité dans la couche de données, là où elles ont leur place. Rien de sur mesure à exploiter — la présence devient un exercice de modélisation de données sur une infrastructure que le backend fait déjà tourner.
