Start · Sprachen · JavaScript · Referenz · Navigator.sendBeacon()

Navigator.sendBeacon()

Methode

Sendet asynchron eine kleine HTTP-POST-Anfrage an einen Server, die auch dann zuverlässig zugestellt wird, wenn die Seite gerade geschlossen oder verlassen wird.

seit JavaScript Baseline: alle modernen Browser (seit 2020 weitgeh Kategorie: webapi

Signatur

Navigator.prototype.sendBeacon(url, data)

Beschreibung

navigator.sendBeacon() ist für Analyse-, Diagnose- und Protokolldaten gedacht, die der Browser noch nach dem Verlassen der Seite zuverlässig verschicken soll. Der Browser stellt die Anfrage in eine Warteschlange und sendet sie im Hintergrund. So werden weder das Entladen der Seite noch die Navigation zur nächsten Seite ausgebremst. Die Methode gibt sofort zurück und liefert nur einen Wahrheitswert: true, wenn die Daten zum Senden angenommen wurden.

Früher griff man für solche Aufgaben zu synchronen XMLHttpRequest-Aufrufen in unload-Handlern oder zu Tricks wie Zählpixeln. Das hat die Navigation spürbar verzögert. sendBeacon() ersetzt diese Muster. Heute gilt ein Aufruf im Ereignis visibilitychange (wenn document.visibilityState === "hidden") oder in pagehide als beste Praxis. Beide Ereignisse werden auf Mobilgeräten deutlich zuverlässiger ausgelöst als unload oder beforeunload.

Die Anfrage ist immer ein POST. Die Antwort des Servers lässt sich nicht auslesen, und es gibt keine Rückruffunktion und kein Promise. Welchen Content-Type die Anfrage bekommt, hängt vom Datentyp ab: Ein String wird als text/plain;charset=UTF-8 gesendet, FormData als multipart/form-data, URLSearchParams als application/x-www-form-urlencoded. Bei einem Blob gilt dessen type.

Parameter

Name Typ Default Beschreibung
$url Pflicht string | URL Ziel-URL, an die die Daten gesendet werden. Relative URLs werden relativ zur Dokument-URL aufgelöst.
$data string | ArrayBuffer | TypedArray | DataView | Blob | FormData | URLSearchParams | null null Die zu sendenden Daten (Anfragekörper). Aus dem Typ ergibt sich der Content-Type der Anfrage. Ohne Angabe wird ein leerer POST gesendet.

Rückgabewert

Typ
boolean
Beschreibung
true, wenn der Browser die Daten erfolgreich in die Sende-Warteschlange aufgenommen hat, sonst false (z. B. weil das Größenlimit überschritten ist). Ob der Server die Daten tatsächlich erhalten hat, sagt der Wert nicht.

Beispiele

Analysedaten beim Verlassen der Seite senden

const sessionStart = performance.now();

document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'hidden') {
    const payload = JSON.stringify({
      page: location.pathname,
      durationMs: Math.round(performance.now() - sessionStart),
    });
    // Blob mit passendem Typ, damit der Server JSON erkennt
    const blob = new Blob([payload], { type: 'application/json' });
    const queued = navigator.sendBeacon('/api/analytics', blob);
    console.log('Beacon eingereiht:', queued);
  }
});
Beacon eingereiht: true

Formulardaten ohne Preflight senden

const params = new URLSearchParams({
  event: 'checkout_abandoned',
  cartId: 'A-1042',
});

// application/x-www-form-urlencoded ist CORS-sicher, daher kein Preflight
const ok = navigator.sendBeacon('https://stats.example.com/collect', params);
console.log(ok);
true

Fallback auf fetch mit keepalive

const sendLog = (url, data) => {
  const body = JSON.stringify(data);
  if (navigator.sendBeacon && navigator.sendBeacon(url, body)) {
    return true;
  }
  // Fallback, z. B. wenn die Daten zu groß sind oder die API fehlt
  fetch(url, {
    method: 'POST',
    body,
    keepalive: true,
    headers: { 'Content-Type': 'text/plain' },
  }).catch(() => {});
  return false;
};

window.addEventListener('pagehide', () => {
  sendLog('/api/log', { type: 'pagehide', ts: Date.now() });
});

Fehler melden (Error-Reporting)

window.addEventListener('error', (event) => {
  const fd = new FormData();
  fd.append('message', event.message);
  fd.append('source', `${event.filename}:${event.lineno}`);
  navigator.sendBeacon('/api/errors', fd);
});

// Wichtig · Fallstricke

  • Größenlimit: Pro Seite dürfen alle noch ausstehenden Beacons zusammen nur eine begrenzte Datenmenge haben, meist rund 64 KiB (dasselbe Kontingent wie bei fetch(..., { keepalive: true })). Ist das Limit überschritten, gibt die Methode false zurück. Den Rückgabewert solltet ihr deshalb prüfen und bei Bedarf auf eine Alternative ausweichen.
  • Keine Antwort: Weder Statuscode noch Antworttext sind zugänglich. Wenn ihr eine Rückmeldung vom Server braucht, nehmt fetch().
  • CORS: Ein Blob mit dem Typ application/json ist kein „einfacher“ Anfragetyp. Bei Zielen auf einer anderen Herkunft (Cross-Origin) löst er einen CORS-Preflight aus, und manche Browser (vor allem ältere Chrome-Versionen) lehnen das ab. Für Cross-Origin-Ziele sind text/plain, URLSearchParams oder FormData sicherer. Beacons senden Cookies mit, und der Server muss bei Cross-Origin passende CORS-Header setzen.
  • Richtiges Ereignis wählen: unload und beforeunload werden besonders auf Mobilgeräten oft gar nicht ausgelöst, und unload kann den Back/Forward-Cache verhindern. Verwendet stattdessen visibilitychange oder pagehide.
  • CSP: Das Ziel muss durch die connect-src-Direktive der Content Security Policy erlaubt sein.
  • Werbe- und Tracking-Blocker blockieren Beacon-Anfragen häufig. Verlasst euch für geschäftskritische Daten also nicht allein darauf.
  • Die Methode muss auf navigator aufgerufen werden. Wer sie herauslöst (const b = navigator.sendBeacon; b(url)), bekommt einen TypeError wegen „Illegal invocation“.