SSE (Server-Sent Events) — це стандарт W3C, що дозволяє серверу надсилати потокові дані клієнту через одне HTTP-з’єднання в односторонньому режимі. На відміну від WebSocket, SSE працює поверх звичайного HTTP і не потребує спеціального протоколу чи бібліотеки на стороні клієнта. Згідно з специфікацією W3C HTML Living Standard (2025), EventSource API підтримується в усіх сучасних браузерах, включаючи Chrome, Firefox, Safari та Edge.
Головне
SSE (Server-Sent Events) — це технологія, яка дозволяє веб-серверу надсилати дані клієнту в будь-який момент після встановлення з’єднання. Вона стандартизована WHATWG як частина HTML Living Standard і використовує тип MIME text/event-stream. SSE підтримує передачу текстових даних з можливістю зазначення ідентифікатора повідомлення, типу події та затримки перепід’єднання.
На відміну від WebSocket, який потребує двостороннього протоколу та запиту на оновлення, SSE працює поверх звичайного HTTP. Сервер встановлює заголовок Content-Type: text/event-stream, надсилає дані частинами та тримає з’єднання відкритим. Клієнт отримує дані через браузерний EventSource API, який автоматично аналізує потік та генерує події.
Згідно з CanIUse (2025), EventSource API підтримується в 97,5% браузерів глобально. Він не підтримується в Internet Explorer та деяких мобільних браузерах (Samsung Internet до версії 7.0). Для цих випадків існують поліфіли, які емулюють EventSource через XHR стрімінг. SSE не працює з HTTP/1.1 pipelining, але повністю сумісний з HTTP/2 server push.
SSE був запропонований як частина специфікації HTML5 у 2009 році під назвою Server-Sent DOM Events. Перша реалізація з’явилася в Opera 9.0, потім у Firefox 6.0 (2011), Chrome 9.0 (2011) та Safari 5.0 (2010). У 2015 році специфікацію було перенесено до окремого розділу HTML Living Standard. Незважаючи на десятилітню історію, SSE залишається менш популярним, ніж WebSocket, через свою односторонню природу.
Механізм роботи SSE наступний: клієнт створює екземпляр EventSource з URL серверної точки закінчення. Браузер надсилає GET-запит із заголовком Accept: text/event-stream. Сервер відповідає статусом 200 OK та заголовком Content-Type: text/event-stream, після чого починає надсилати дані у форматі event-stream. З’єднання залишається відкритим, поки сервер не надішле сигнал завершення або клієнт не викличе close().
На стороні сервера дані надсилаються частинами (chunked transfer encoding). Кожен блок даних — це текстове повідомлення, що складається з рядків полів (event, data, id, retry). Сервер може надсилати повідомлення в будь-який момент, що робить SSE ідеальним для сповіщень та оновлень статусу. З’єднання не потребує постійного обміну пакетами heartbeat (як WebSocket), хоча поле retry керує частотою перепід’єднання.
Згідно з тестами продуктивності (2024), SSE забезпечує пропускну здатність до 10 000 повідомлень на секунду на одне з’єднання при розмірі повідомлення 256 байтів. На стороні сервера кожне SSE-з’єднання споживає приблизно 5–10 КБ пам’яті, що дозволяє одному серверу підтримувати 50 000+ одночасних з’єднань з 1 ГБ RAM. Це значно менше, ніж у WebSocket, через відсутність бінарного протоколу.
Формат text/event-stream — це простий текстовий протокол, де кожне повідомлення складається з іменованих полів, розділених символами нового рядка. Кожне поле має формат «Ім’я поля: значення». Повідомлення розділяються двома символами нового рядка (\n\n).
Підтримувані поля: event (тип події, типово message), data (рядок даних, може бути багаторядковим), id (останній ідентифікатор події, зберігається в Last-Event-ID), retry (час перепід’єднання в мілісекундах). Коментарі починаються з двокрапки (:) та ігноруються парсером, але можуть використовуватися для heartbeat.
| Поле | Обов’язкове | Призначення |
|---|---|---|
| event | Ні | Тип події (типово message) |
| data | Так | Рядок даних повідомлення |
| id | Ні | Ідентифікатор події для Last-Event-ID |
| retry | Ні | Затримка перепід’єднання в мс |
: heartbeat comment
event: update
data: {"user": "Alice", "action": "typing"}
id: 1001
event: notification
data: {"type": "info", "text": "New version available"}
data: {"type": "action", "url": "/upgrade"}
retry: 3000
event: close
data: Session ended
EventSource API — це вбудований браузерний інтерфейс для прийому SSE. Для створення з’єднання достатньо викликати конструктор з URL точки закінчення. EventSource автоматично встановлює з’єднання, обробляє перепід’єднання та аналізує вхідні повідомлення в JavaScript-події.
Події EventSource: open (з’єднання встановлено), message (отримано повідомлення без зазначення події), error (помилка з’єднання). Для користувацьких подій (event: custom) можна використовувати addEventListener з іменем події. EventSource автоматично надсилає заголовок Last-Event-ID при перепід’єднанні, що дозволяє серверу відновити потік з перерваного місця.
Згідно з документацією MDN (2025), EventSource підтримує CORS та передачу облікових даних (withCredentials). EventSource не підходить для надсилання користувацьких заголовків або тіла запиту — потрібна ручна реалізація через fetch + ReadableStream. EventSource не підтримує бінарні дані — тільки текст та JSON.
const eventSource = new EventSource('/api/events/stream');
eventSource.addEventListener('open', () => {
console.log('З’єднання SSE відкрито');
});
eventSource.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
console.log('Отримано:', data);
renderUpdate(data);
});
eventSource.addEventListener('notification', (event) => {
const notification = JSON.parse(event.data);
showNotification(notification.text);
});
eventSource.addEventListener('error', (error) => {
console.error('Помилка SSE:', error);
// Браузер автоматично перепід’єднується
});
// Закрити з’єднання
eventSource.close();
SSE та WebSocket — різні технології для real-time комунікації, кожна зі своїми сильними сторонами. WebSocket підходить для двостороннього обміну даними (чати, ігри, спільне редагування), SSE — для односторонніх потоків від сервера до клієнта (сповіщення, стрічки новин, тікери).
Ключова відмінність — WebSocket потребує запиту на оновлення з HTTP/1.1 до протоколу WebSocket (ws://), що може блокуватися корпоративними проксі. SSE працює поверх звичайного HTTP, проходить через будь-які проксі та не потребує спеціальної конфігурації сервера. SSE також простіший у реалізації — серверу не потрібна додаткова бібліотека, достатньо правильно сформувати HTTP-відповідь.
Згідно з порівняльним тестуванням (2024), на одному серверному процесі SSE підтримує на 30–50% більше з’єднань, ніж WebSocket, через простіший протокол. Однак latency у SSE вища (50–200 мс проти 10–50 мс у WebSocket) через те, що SSE використовує chunked HTTP, а не повноцінний двонаправлений потік з бінарними фреймами.
| Характеристика | SSE | WebSocket |
|---|---|---|
| Напрям | Сервер → клієнт | Двосторонній |
| Протокол | HTTP (text/event-stream) | ws:// / wss:// (RFC 6455) |
| Браузери | 97,5% (вбудований EventSource) | 97% (вбудований WebSocket) |
| Дані | Тільки текст / JSON | Текст + бінарні (Blob, ArrayBuffer) |
| Обробка проксі | Проходить будь-які проксі | Потребує налаштування проксі |
| Перепід’єднання | Автоматичне (браузер) | Ручна реалізація |
| Історія | Last-Event-ID | Немає вбудованої історії |
Реалізація SSE на сервері не потребує бібліотек — достатньо встановити правильні HTTP-заголовки та надсилати дані у форматі text/event-stream. Розглянемо приклад на Node.js з використанням вбудованого модуля http. Сервер встановлює заголовки Content-Type та Cache-Control, після чого надсилає повідомлення кожні N секунд.
Згідно з MDN Web Docs (2025), обов’язкові заголовки для SSE: Content-Type: text/event-stream, Cache-Control: no-cache та Connection: keep-alive. Без Cache-Control браузер може закешувати SSE-потік, що призведе до зупинки доставки. Connection: keep-alive явно вказує браузеру тримати з’єднання відкритим.
const http = require('http');
http.createServer((req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
let eventId = 0;
const interval = setInterval(() => {
eventId++;
res.write(`id: ${eventId}\n`);
res.write(`event: update\n`);
res.write(`data: {"time": "${new Date().toISOString()}", "id":${eventId}}\n\n`);
}, 2000);
req.on('close', () => {
clearInterval(interval);
});
}).listen(3000);
from flask import Response, Flask
import time
import json
app = Flask(__name__)
@app.route('/stream')
def stream():
def generate():
event_id = 0
while True:
event_id += 1
data = json.dumps(
{'ticker': 'AAPL', 'price': 150.25})
yield f'id: {event_id}\nevent: price\ndata: {data}\n\n'
time.sleep(1)
return Response(generate(),
mimetype='text/event-stream')
Використання SSE в мобільних додатках обмежено відсутністю нативної реалізації EventSource для iOS та Android. На мобільних платформах SSE реалізується через сторонні бібліотеки: на iOS — через URLSession з NSURLProtocol, на Android — через OkHttp з SSE-підтримкою (okhttp-sse). Для React Native та Flutter доступні пакети, які емулюють EventSource.
На iOS нативна реалізація SSE можлива через URLSessionDataDelegate. При отриманні даних у методі urlSession(_:dataTask:didReceive:) додаток накопичує буфер і аналізує event-stream формат вручну. Згідно з блогом iOS-розробки (2024), споживання батареї при SSE на iOS на 40% нижче, ніж при постійному WebSocket-з’єднанні, через відсутність heartbeat-пакетів.
На Android OkHttp надає клас EventSource.Factory для підписки на SSE-потоки. Android-додатки можуть використовувати SSE для сповіщень, коли FCM недоступний, або для синхронізації даних у фоновому режимі. SSE на Android добре працює з WorkManager для довгоживучих фонових завдань. Згідно з документацією OkHttp (2025), okhttp-sse підтримує автоматичне перепід’єднання з кастомним listener.
Часто задавані питання
SSE — одностороння передача (сервер → клієнт) поверх HTTP, не потребує бібліотек на клієнті. WebSocket — двостороння передача з бінарним протоколом. SSE простіший у реалізації, WebSocket підходить для завдань, де клієнт теж надсилає дані.
Ні, SSE передає тільки текстові дані. Для бінарних даних (зображення, аудіо) потрібне кодування Base64, яке збільшує розмір на 33%. Для бінарних потоків краще використовувати WebSocket.
EventSource автоматично перепід’єднується при обриві. Час затримки задається полем retry в потоці (типово 1000 мс). При перепід’єднанні браузер надсилає заголовок Last-Event-ID, що дозволяє серверу відновити потік з перерваного місця.
Кожен браузер має обмеження на кількість одночасних HTTP-з’єднань з одним доменом. Для HTTP/1.1 — 6–8 з’єднань на домен, для HTTP/2 — до 100. SSE використовує одне з’єднання, тому конкуренції з іншими запитами не виникає.
SSE підходить тільки для отримання повідомлень (вхідні). Для надсилання повідомлень (вихідні) потрібен окремий HTTP-запит (POST). Для повноцінного чату зручніше використовувати WebSocket або Socket.IO з двостороннім зв’язком в одному з’єднанні.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також