Server-Sent Events (SSE) für REDAXO-Backend-Funktionen.
Dieses AddOn ist aktuell ein Experiment. Es dient dazu, den Einsatz von Server-Sent Events (SSE) in REDAXO praxisnah zu demonstrieren und verschiedene Streaming-Szenarien im Backend zu testen.
Projekt-Repository:
https://github.com/FriendsOfREDAXO/sse
- Fortschritt (
sse_progress) - Chat (
sse_chat,sse_chat_send,sse_chat_clear) - To-do (
sse_todos) - Benutzerstatus (
sse_users,sse_heartbeat) - Monitor (
sse_monitor) - Medienverarbeitung (
sse_media) - Symfony-Streams (
sse_symfony,sse_symfony_old) - Logs (
sse_logs) - Datenstream (
sse_data)
Damit SSE stabil funktioniert, sollte der Server folgende Voraussetzungen erfüllen:
- REDAXO
^5.18 - PHP
^8.3 - HTTP/1.1 oder HTTP/2 mit Keep-Alive
- Reverse Proxy darf SSE-Antworten nicht puffern
- Komprimierung für
text/event-streamsollte deaktiviert sein - Ausreichende Timeouts (empfohlen:
300bis3600Sekunden)
Wichtig bei SSE:
- Content-Type muss
text/event-streamsein - Antwort muss kontinuierlich gestreamt werden (kein Full-Response-Buffer)
- Caching sollte für SSE deaktiviert sein
Typisches Setup in Plesk: NGINX als Reverse Proxy vor Apache/PHP.
Kurzantwort auf die häufige Frage: Ja, primär konfigurierst du NGINX. Wenn Apache im Stack aktiv ist, solltest du zusätzlich Apache-Komprimierung für SSE deaktivieren und die PHP-Limits prüfen.
Pfad in Plesk:
- Domains -> deine Domain -> Apache & nginx-Einstellungen
- Feld: Zusätzliche nginx-Anweisungen
Dort ergänzen:
location = /redaxo/index.php {
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off;
proxy_request_buffering off;
proxy_cache off;
gzip off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
add_header X-Accel-Buffering no;
}Pfad in Plesk:
- Domains -> deine Domain -> Apache & nginx-Einstellungen
- Feld: Zusätzliche Apache-Anweisungen
Dort ergänzen:
<IfModule mod_headers.c>
SetEnvIfExpr "%{REQUEST_URI} == '/redaxo/index.php' && %{QUERY_STRING} =~ /(^|&)rex-api-call=sse_/" is_sse_call=1
Header always set Cache-Control "no-cache, no-transform" env=is_sse_call
</IfModule>
<IfModule mod_deflate.c>
SetEnvIfExpr "%{REQUEST_URI} == '/redaxo/index.php' && %{QUERY_STRING} =~ /(^|&)rex-api-call=sse_/" no-gzip=1
</IfModule>Pfad in Plesk:
- Domains -> deine Domain -> PHP-Einstellungen
Prüfen/setzen:
- max_execution_time ausreichend hoch (z. B. 300 bis 3600)
- output_buffering möglichst aus oder klein
- zlib.output_compression aus (für SSE)
- NGINX-Regeln sind bei Proxy-Setup zwingend.
- Apache-Regeln sind bei aktivem Apache sehr sinnvoll, damit Events nicht komprimiert/gepuffert werden.
- PHP-Limits entscheiden mit, ob Streams stabil offen bleiben.
Hinweis:
- Diese Regel greift für Aufrufe über
/redaxo/index.php. - Falls in der Umgebung weitere Proxy-Layer aktiv sind (CDN/WAF), dort ebenfalls Buffering und Caching für SSE deaktivieren.
- Je nach Stack (Apache-only, NGINX-Plugin, LiteSpeed) gibt es meist einen Bereich wie
Include Editor,Custom DirectivesoderAdditional NGINX directives. - Ziel ist immer gleich: Buffering aus, Cache aus, Timeouts hoch, keine Komprimierung für
text/event-stream.
- In den Website-Einstellungen unter
Nginx Directivesbzw.Apache Directivesentsprechende Regeln ergänzen. - Nach Änderungen Webserver-Konfiguration neu laden.
- Im vHost-Template oder in den zusätzlichen Domain-Direktiven die SSE-Regeln setzen.
- Falls FastCGI-Buffering aktiv ist: für SSE deaktivieren.
location = /redaxo/index.php {
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off;
proxy_request_buffering off;
proxy_cache off;
gzip off;
proxy_read_timeout 3600s;
add_header X-Accel-Buffering no;
}Wenn Apache direkt ausliefert oder hinter einem Proxy läuft, sollten die Regeln auf SSE-Requests begrenzt werden:
<IfModule mod_headers.c>
SetEnvIfExpr "%{REQUEST_URI} == '/redaxo/index.php' && %{QUERY_STRING} =~ /(^|&)rex-api-call=sse_/" is_sse_call=1
Header always set Cache-Control "no-cache, no-transform" env=is_sse_call
Header always set X-Accel-Buffering "no" env=is_sse_call
</IfModule>
<IfModule mod_deflate.c>
SetEnvIfExpr "%{REQUEST_URI} == '/redaxo/index.php' && %{QUERY_STRING} =~ /(^|&)rex-api-call=sse_/" no-gzip=1
</IfModule>Danach:
- Konfiguration testen (
nginx -tbzw.apachectl configtest) - Dienste neu laden/restarten
- SSE-Endpoint im Browser testen und auf fortlaufende Events achten
Endpoint:
/redaxo/index.php?rex-api-call=sse_data
Unterstützte Parameter:
source:article_status,system_metrics,clockinterval: Sekundenintervall (1..30)duration: Gesamtlaufzeit in Sekunden (10..3600)
Event-Typen:
initdatacomplete
Beispiel:
/redaxo/index.php?rex-api-call=sse_data&source=article_status&interval=5&duration=600
Die einfachste Nutzung im Frontend erfolgt mit EventSource.
<div id="sse-status">Verbinde...</div>
<pre id="sse-output"></pre>
<script>
const statusEl = document.getElementById('sse-status');
const outputEl = document.getElementById('sse-output');
const endpoint = '/redaxo/index.php?rex-api-call=sse_data&source=clock&interval=2&duration=120';
const stream = new EventSource(endpoint);
stream.onopen = () => {
statusEl.textContent = 'Verbunden';
};
stream.onmessage = (event) => {
outputEl.textContent += event.data + '\n';
};
stream.onerror = () => {
statusEl.textContent = 'Verbindung unterbrochen - Reconnect läuft automatisch';
};
</script>Hinweis: EventSource versucht bei Verbindungsabbrüchen automatisch einen Reconnect.
Das AddOn liefert bei sse_data z. B. die Event-Typen init, data, complete.
<script>
const endpoint = '/redaxo/index.php?rex-api-call=sse_data&source=system_metrics&interval=5&duration=300';
const stream = new EventSource(endpoint);
stream.addEventListener('init', (event) => {
console.log('Init:', JSON.parse(event.data));
});
stream.addEventListener('data', (event) => {
const payload = JSON.parse(event.data);
console.log('Daten:', payload);
});
stream.addEventListener('complete', (event) => {
console.log('Fertig:', JSON.parse(event.data));
stream.close();
});
</script>const params = new URLSearchParams({
'rex-api-call': 'sse_data',
source: 'article_status',
interval: '3',
duration: '180'
});
const endpoint = '/redaxo/index.php?' + params.toString();
const stream = new EventSource(endpoint);Dieses Beispiel hält einen Statusbereich automatisch aktuell und zeigt den letzten Zeitstempel aus dem Stream.
<div id="live-box" style="padding:12px;border:1px solid #ddd;border-radius:8px;max-width:420px;">
<strong>Live-Status:</strong>
<div id="live-value">Warte auf Daten...</div>
<small id="live-meta">Noch kein Update</small>
</div>
<script>
const valueEl = document.getElementById('live-value');
const metaEl = document.getElementById('live-meta');
const endpoint = '/redaxo/index.php?rex-api-call=sse_data&source=clock&interval=2&duration=1800';
const stream = new EventSource(endpoint);
stream.addEventListener('data', (event) => {
try {
const payload = JSON.parse(event.data);
const value = payload.value ?? payload.time ?? event.data;
valueEl.textContent = String(value);
metaEl.textContent = 'Letztes Update: ' + new Date().toLocaleTimeString('de-DE');
} catch (error) {
valueEl.textContent = event.data;
metaEl.textContent = 'Letztes Update: ' + new Date().toLocaleTimeString('de-DE');
}
});
stream.onerror = () => {
metaEl.textContent = 'Verbindung unterbrochen, Reconnect läuft ...';
};
window.addEventListener('beforeunload', () => {
stream.close();
});
</script>- Pro Browser-Tab nur so viele Streams öffnen wie nötig.
- Streams beim Verlassen einer Seite schließen (
stream.close()). - JSON robust parsen (
try/catch), da auch Status- oder Fehlerevents auftreten können. - Bei Login-geschützten Endpoints sicherstellen, dass Session/Cookies vorhanden sind.