Echtzeit-Funktionen mit PHP: WebSockets, SSE & Live-Dashboards
Technologie & Architektur

Echtzeit-Funktionen mit PHP: WebSockets, SSE & Live-Dashboards

Carola Schulte
2. Februar 2026
12 min

Echtzeit-Funktionen mit PHP: WebSockets, SSE & Live-Dashboards

“Der Kunde will, dass sich das Dashboard automatisch aktualisiert. Ohne Reload.” - Diesen Satz höre ich in fast jedem zweiten Projekt. Die Erwartung an Web-Apps hat sich verändert: Nutzer wollen Live-Daten, sofortige Benachrichtigungen und Dashboards, die sich von selbst aktualisieren.

Das Problem: Klassisches HTTP ist Request-Response. Der Browser fragt, der Server antwortet. Ohne Anfrage passiert nichts. Für Echtzeit brauchen Sie einen Rückkanal - und da gibt es drei grundlegend verschiedene Ansätze.


Für Entscheider: Warum Echtzeit Geld spart

Echtzeit-Funktionen sind kein technisches Spielzeug. Sie lösen konkrete Business-Probleme:

ProblemOhne EchtzeitMit Echtzeit
Lager-DashboardMitarbeiter drückt F5, sieht veraltete BeständeBestand aktualisiert sich sofort nach Buchung
Support-TicketsystemZwei Mitarbeiter bearbeiten dasselbe TicketLive-Anzeige “wird gerade bearbeitet von…”
AuftragsübersichtChef prüft alle 10 Minuten den StatusPush-Benachrichtigung bei Statusänderung
MonitoringFehler werden erst bei nächster Prüfung entdecktSofort-Alarm bei Grenzwert-Überschreitung

Noch teurer als die Wartezeit sind Fehler durch veraltete Daten, doppelte Arbeit und falsche Entscheidungen. Ein Mitarbeiter sieht “15 auf Lager”, ein anderer hat gerade 12 davon verbucht. Ein Disponent plant mit Beständen, die seit einer Stunde nicht mehr stimmen. Dazu kommt die reine Zeitverschwendung: Wenn 20 Mitarbeiter jeweils 30x am Tag F5 drücken und jedes Mal 5 Sekunden warten, ist das fast eine Arbeitsstunde täglich - nur fürs Neuladen.


Die drei Ansätze im Vergleich

1. Polling: Der einfachste Ansatz

Der Browser fragt regelmäßig beim Server nach: “Gibt es was Neues?”

// Alle 5 Sekunden den Server fragen
const pollInterval = setInterval(async () => {
    const response = await fetch('/api/dashboard-data');
    const data = await response.json();
    updateDashboard(data);
}, 5000);

// Wichtig: Bei Seitenwechsel oder Component-Unmount aufräumen
// clearInterval(pollInterval);

Vorteile: Trivial zu implementieren, funktioniert mit jedem Server, kein besonderes Setup.

Nachteile: Unnötige Last (99% der Requests liefern “keine Änderung”), Verzögerung bis zum nächsten Intervall, skaliert schlecht (100 Nutzer × alle 5 Sekunden = 20 Requests/Sekunde für nichts).

Wann Polling reicht: Wenn Daten sich selten ändern (alle paar Minuten), wenige gleichzeitige Nutzer (unter 50) und Sie keine Infrastruktur für WebSockets/SSE aufbauen wollen.

2. Server-Sent Events (SSE): Der Einweg-Kanal

Der Server schickt Daten an den Browser, wann immer es etwas Neues gibt. Der Browser hört zu - ohne ständig zu fragen.

// PHP: SSE-Endpoint
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');

while (true) {
    $data = getLatestUpdates(); // Ihre Datenquelle
    if ($data) {
        echo "data: " . json_encode($data) . "\n\n";
        ob_flush();
        flush();
    }
    sleep(1); // 1 Sekunde warten, dann erneut prüfen
}
// Browser: SSE empfangen
const source = new EventSource('/api/stream');

source.onmessage = (event) => {
    const data = JSON.parse(event.data);
    updateDashboard(data);
};

source.onerror = () => {
    // Reconnect passiert automatisch
    console.log('Verbindung unterbrochen, reconnecting...');
};

Wichtig: In echten Projekten braucht ein SSE-Endpoint zusätzlich Heartbeats (regelmäßige Keep-Alive-Nachrichten, damit Proxies die Verbindung nicht schließen) und sauberes Timeout-/Disconnect-Handling. Die Snippets oben zeigen das Grundprinzip, nicht den Production-Code.

Vorteile: Einfach zu implementieren (reines HTTP), automatischer Reconnect eingebaut, nativ in allen modernen Browsern. SSE lässt sich auch mit klassischem PHP umsetzen - allerdings nur sinnvoll für moderate Nutzerzahlen und mit Blick auf blockierte Worker.

Nachteile: Nur Server → Browser (kein Rückkanal), pro SSE-Verbindung blockiert ein PHP-Prozess. SSE funktioniert heute in modernen Browsern gut. Bei älteren Setups mit HTTP/1.1 können parallele Verbindungen pro Domain jedoch begrenzt sein (typisch 6 pro Domain).

Wann SSE die richtige Wahl ist: Live-Dashboards, Benachrichtigungen, Monitoring, Newsfeeds - überall, wo der Server Daten pusht und der Client nur zuhört.

3. WebSockets: Bidirektionale Verbindung

Permanente Vollduplex-Verbindung - Server und Client können gleichzeitig und jederzeit Daten senden und empfangen.

// Browser: WebSocket
const ws = new WebSocket('wss://example.com/ws');

ws.onopen = () => {
    // Authentifizierung senden
    ws.send(JSON.stringify({ type: 'auth', token: '...' }));
};

ws.onmessage = (event) => {
    const data = JSON.parse(event.data);
    handleRealtimeUpdate(data);
};

// Daten senden (z.B. Chat-Nachricht)
function sendMessage(text) {
    ws.send(JSON.stringify({ type: 'message', text }));
}

Vorteile: Bidirektional (Client ↔ Server), minimaler Overhead nach dem Handshake, ideal für hohe Frequenz (Chat, Gaming, Collaboration).

Nachteile: WebSockets lassen sich im PHP-Umfeld meist nicht sinnvoll mit klassischem Request/Response-Betrieb lösen. In der Praxis kommt dafür fast immer ein separater, langlebiger Prozess oder eigener Server dazu. Komplexere Infrastruktur (Reverse Proxy, Load Balancing), kein automatischer Reconnect (müssen Sie selbst bauen).

Wann WebSockets nötig sind: Chat, kollaboratives Editing (mehrere Nutzer bearbeiten gleichzeitig), Gaming, Echtzeit-Collaboration à la Google Docs.


Entscheidungshilfe

KriteriumPollingSSEWebSockets
RichtungClient → ServerServer → ClientBidirektional
ImplementierungTrivialEinfachKomplex
PHP-kompatibelJaJa (mit Einschränkungen)Braucht separaten Server
LatenzSekunden (Intervall)MillisekundenMillisekunden
SkalierungSchlecht (unnötige Last)MittelGut
Browser-SupportAlleAlle modernenAlle modernen
ReconnectEingebaut (nächstes Intervall)AutomatischSelbst implementieren

Meine Faustregel:

  • Dashboard, Monitoring, Benachrichtigungen → SSE (einfach, reicht fast immer)
  • Chat, Collaboration, Gaming → WebSockets (bidirektional nötig)
  • Seltene Updates, wenige Nutzer → Polling (warum komplizierter als nötig?)

Praxis: Live-Dashboard mit SSE und PHP

Ein typisches Szenario: Auftragsübersicht, die sich automatisch aktualisiert, wenn ein neuer Auftrag eingeht oder ein Status sich ändert.

Architektur

Browser                    Server
  │                          │
  │── GET /api/stream ──────>│  (SSE-Verbindung öffnen)
  │                          │
  │<── data: {neue Auftr.} ──│  (sofort bei Änderung)
  │<── data: {Status upd.} ──│  (sofort bei Änderung)
  │                          │
  │  (Verbindung bleibt offen, Server pusht bei Bedarf)

Das PHP-Problem: Blockierende Prozesse

Standard-PHP (Apache mod_php, PHP-FPM) ist für kurze Request-Response-Zyklen gebaut. Eine SSE-Verbindung, die minutenlang offen bleibt, blockiert einen PHP-Worker-Prozess.

Bei 20 gleichzeitigen Dashboard-Nutzern: 20 blockierte PHP-Prozesse. Standard PHP-FPM hat oft nur 10-30 Worker konfiguriert. Das wird eng.

Lösungen:

Die folgenden Angaben sind Daumenregeln, keine harten Architekturgrenzen - die tatsächliche Grenze hängt von Server-Ausstattung, Datenvolumen und Update-Frequenz ab:

AnsatzAufwandTypischer Einsatz
PHP-FPM Worker erhöhenMinimalKleine interne Tools
Datenbank-Polling im SSEGeringModerate Last, typische Business-Apps
Redis Pub/Sub + SSEMittelGrößere Multi-User-Setups
Separater SSE-Server (Node.js, Go)HochHohe Last, mehrere Server

Für die meisten Business-Apps reicht der einfache Ansatz: PHP-FPM Worker auf 50-100 setzen und im SSE-Endpoint per Datenbank-Polling (alle 1-2 Sekunden) auf Änderungen prüfen. Das ist nicht elegant, aber es funktioniert zuverlässig für interne Tools mit 20-50 gleichzeitigen Nutzern.

Änderungen erkennen: Drei Ansätze

Woher weiß der SSE-Endpoint, dass sich etwas geändert hat?

A) Timestamp-basiert (einfach):

// Letzte Änderung prüfen
$lastCheck = time();
while (true) {
    $updates = $db->query(
        "SELECT * FROM auftraege WHERE updated_at > to_timestamp($1)",
        [$lastCheck]
    );
    if ($updates) {
        echo "data: " . json_encode($updates) . "\n\n";
        flush();
        $lastCheck = time();
    }
    sleep(1);
}

Für Entwickler: PostgreSQL LISTEN/NOTIFY

-- 1. Trigger-Funktion anlegen
CREATE OR REPLACE FUNCTION notify_order_change()
RETURNS trigger AS $$
BEGIN
    PERFORM pg_notify('order_updates', json_build_object(
        'action', TG_OP,
        'order_id', NEW.id
    )::text);
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- 2. Trigger an Tabelle binden
CREATE TRIGGER order_change_trigger
    AFTER INSERT OR UPDATE ON auftraege
    FOR EACH ROW EXECUTE FUNCTION notify_order_change();
// 3. PHP: Auf Notifications lauschen (SSE-Endpoint)
$db = pg_connect("host=localhost dbname=app");
pg_query($db, "LISTEN order_updates");

header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');

while (true) {
    $result = pg_get_notify($db);
    if ($result) {
        echo "data: " . $result['payload'] . "\n\n";
        ob_flush();
        flush();
    }
    usleep(100000); // 100ms warten
}

Kein Polling gegen die Datenbank nötig - PostgreSQL pusht die Änderung aktiv. Deutlich effizienter, braucht aber eine persistente DB-Verbindung pro SSE-Client.

C) Redis Pub/Sub (für größere Setups):

// Schreibvorgang: Nachricht in Redis publizieren
$redis = new Redis();
$redis->connect('127.0.0.1');
$redis->publish('order_updates', json_encode([
    'action' => 'update',
    'order_id' => $orderId
]));

Alle SSE-Endpoints subscriben den Redis-Channel. Vorteil gegenüber PostgreSQL NOTIFY: Funktioniert auch über mehrere Server hinweg und entkoppelt die Echtzeit-Kommunikation von der Datenbank. Erfordert aber einen Redis-Server und die PHP-Extension phpredis.


Häufige Fallen

1. SSE hinter Reverse Proxy bricht ab

Nginx buffert Server-Responses standardmäßig. SSE-Streams werden dadurch blockiert, bis der Buffer voll ist.

Lösung: In der Nginx-Config für den SSE-Endpoint:

location /api/stream {
    proxy_buffering off;
    proxy_cache off;
    proxy_read_timeout 86400s;  # 24h statt 60s
}

2. Authentifizierung bei SSE

SSE nutzt EventSource - das unterstützt keine Custom-Headers. Sie können keinen Authorization: Bearer-Header mitsenden.

Lösung: Cookie-basierte Authentifizierung (sauberer, weil das Token nicht in der URL und damit nicht in Server-Logs landet) oder Token als Query-Parameter (/api/stream?token=..., einfacher).

// Cookie-basierte Auth im SSE-Endpoint
session_start();
if (!isset($_SESSION['user_id'])) {
    http_response_code(401);
    exit('Unauthorized');
}

header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
// ... SSE-Stream wie oben

3. Reconnect-Logik fehlt bei WebSockets

SSE hat automatischen Reconnect. WebSockets nicht. Wenn die Verbindung abbricht (WLAN-Wechsel, Server-Restart), bleibt der Client stumm.

Minimum:

let shouldReconnect = true;

function connectWS() {
    const ws = new WebSocket('wss://...');
    ws.onclose = () => {
        if (shouldReconnect) {
            setTimeout(connectWS, 3000);
        }
    };
    return ws;
}

// Bei Logout: Reconnect verhindern
function logout() {
    shouldReconnect = false;
    ws.close();
}

Für Production-Systeme empfiehlt sich zusätzlich exponentielles Backoff (3s, 6s, 12s…) und ein maximaler Retry-Count, damit der Client nicht endlos gegen einen toten Server anläuft.

4. Zu viele Updates fluten das Frontend

Wenn sich Daten 50x pro Sekunde ändern, müssen Sie das Frontend nicht 50x pro Sekunde updaten. Das führt zu Ruckeln und hoher CPU-Last im Browser.

Lösung: Throttling - Daten sammeln, gebündelt rendern:

let pendingUpdates = [];
let throttleTimer = null;

source.onmessage = (event) => {
    pendingUpdates.push(JSON.parse(event.data));

    if (!throttleTimer) {
        throttleTimer = setTimeout(() => {
            updateDashboard(pendingUpdates);
            pendingUpdates = [];
            throttleTimer = null;
        }, 500); // Maximal alle 500ms ein DOM-Update
    }
};

Wann sich der Aufwand lohnt - und wann nicht

Echtzeit lohnt sich:

  • Mehrere Nutzer arbeiten mit denselben Daten (Kollisionsvermeidung)
  • Zeitkritische Informationen (Monitoring, Alerts, Auftragseingang)
  • Nutzer lassen die App den ganzen Tag offen (Dashboards, Support-Tools)

Echtzeit ist Overkill:

  • Daten ändern sich selten (einmal pro Stunde reicht ein manueller Refresh)
  • Nur ein Nutzer arbeitet im System (keine Kollisionen möglich)
  • Informationen sind nicht zeitkritisch (Monatsreport braucht kein Live-Update)

Mein pragmatischer Ansatz für die meisten Business-Apps: SSE für Live-Updates im Dashboard, klassisches HTTP für alles andere. Keine extra Infrastruktur, kein WebSocket-Server, kein Redis - solange die Nutzerzahl unter 100 bleibt.


Checkliste: Echtzeit in Ihrer Web-App

  • Brauchen Sie wirklich Echtzeit? Oder reicht ein Refresh-Button?
  • Welche Richtung? Server → Client (SSE) oder bidirektional (WebSocket)?
  • Wie viele gleichzeitige Nutzer? Unter 50: SSE mit PHP. Über 100: separater Server.
  • Nginx/Reverse Proxy konfiguriert? Buffering aus für SSE-Endpoints.
  • Reconnect-Logik vorhanden? Besonders bei WebSockets (mit Backoff und max. Retries).
  • Throttling im Frontend? Nicht jedes Update muss sofort gerendert werden.
  • Authentifizierung gelöst? SSE: Cookie oder Token-Parameter. WebSocket: im Handshake.
  • Error-Handling eingebaut? SSE und WebSocket-Verbindungen können jederzeit fehlschlagen.
  • Load-Testing durchgeführt? Wie viele gleichzeitige SSE-Verbindungen hält Ihr Server?
  • Monitoring vorhanden? Offene SSE/WebSocket-Verbindungen tracken, tote Verbindungen erkennen.

Unsicher, ob SSE oder WebSockets für Ihr Projekt passen? Ich schaue mir Ihre Anforderungen an und empfehle den einfachsten Ansatz, der funktioniert - kostenloses Erstgespräch anfragen.

Carola Schulte

Über Carola Schulte

Software-Architektin mit 25+ Jahren Erfahrung. Spezialisiert auf robuste Business-Apps mit PHP/PostgreSQL, Security-by-Design und DSGVO-konforme Systeme. 1,8M+ Lines of Code in Produktion.

Projekt im Kopf?

Lassen Sie uns besprechen, wie ich Ihre Anforderungen umsetzen kann – kostenlos und unverbindlich.

Kostenloses Erstgespräch