SSE-события приходят пачками: как найти буферизацию в nginx и прокси

Практический порядок проверки SSE-потока: от формата события и flush в приложении до nginx, CDN и кода браузера.

Если SSE-события приходят не по одному, а пачками после заметной паузы, соединение обычно не оборвано: один из слоёв копит небольшие фрагменты ответа и отдаёт их вместе. Проверяйте цепочку по порядку: приложение → nginx → внешний прокси или CDN → браузер.

Начните с прямого запроса к приложению. Если поток задерживается уже там, исправляйте формирование ответа и сброс буферов; если прямой поток ровный, сравнивайте его с ответом через каждый следующий прокси.

Сначала исключите ошибку формата SSE

Для SSE нужен ответ с типом text/event-stream. Каждое событие состоит из строк полей и заканчивается пустой строкой. Пока разделитель не получен, EventSource не обязан передавать сообщение обработчику, поэтому пропущенная пустая строка выглядит точно как буферизация.

Добавьте в тестовые события последовательный номер и серверное время. В браузере записывайте номер и время получения. Так станет видно, где возникла задержка, а не только момент, когда интерфейс обновился.

Пример минимального события:

id: 17
event: status
data: {"state":"ready","sentAt":"SERVER_TIMESTAMP"}

После строки data здесь есть пустая строка. Не помещайте в тестовый поток токены, персональные данные и внутренние адреса.

Шаг 1. Проверьте приложение без публичного прокси

Запустите запрос с того же сервера или из разрешённой внутренней сети прямо к upstream-приложению:

curl -N -i http://127.0.0.1:PORT/events

Ключ -N отключает буферизацию вывода у самого curl. Смотрите не только на заголовки: несколько тестовых событий должны появляться с ожидаемым интервалом, пока соединение остаётся открытым.

Если события уже на этом шаге выходят пачками, проверьте:

  1. заканчивается ли каждое событие пустой строкой;
  2. действительно ли фреймворк передаёт очередной фрагмент в веб-сервер, а не держит всё тело ответа;
  3. нет ли буфера вывода в PHP, приложении или middleware;
  4. не собирает ли компрессор маленькие фрагменты перед отправкой;
  5. не удерживает ли долгий запрос сессию и связанные запросы пользователя.

Одного вызова функции flush может быть недостаточно: данные способны остаться в буфере более высокого уровня. Повторяйте тест после каждого изменения и отмечайте первый слой, на котором интервалы стали ровными.

Шаг 2. Сравните ответ через nginx

Когда прямой upstream работает правильно, повторите тот же тест через локальный адрес nginx. Для SSE-маршрута конфигурация обычно должна отключать буферизацию ответа и кэширование именно в этом location:

location /events {
    proxy_pass http://app;
    proxy_buffering off;
    proxy_cache off;
}

Не отключайте буферизацию для всего сайта: обычным страницам она может быть полезна. И не пытайтесь лечить поток только уменьшением proxy_buffers — это меняет размер накопления, но не устраняет сам режим.

Приложение также может вернуть X-Accel-Buffering: no. nginx использует этот заголовок для управления буферизацией, но обычно не передаёт служебные X-Accel-* клиенту. Поэтому отсутствие заголовка в DevTools ещё не доказывает, что приложение его не отправило: проверяйте ответ upstream и эффективную конфигурацию nginx отдельно.

Если подозрение падает на сжатие, временно отключите его только для тестового SSE-маршрута и снова сравните интервалы. Перед применением конфигурации выполните nginx -t, затем используйте штатную перезагрузку и держите готовым откат.

Шаг 3. Найдите внешний слой, который собирает чанки

Если через локальный nginx события идут ровно, а через публичный домен — пачками, задержка появилась дальше: в балансировщике, WAF, ingress-контроллере, CDN или ещё одном reverse proxy.

Сравнивайте один и тот же тестовый поток с одинаковыми номерами событий. Для внешнего слоя проверьте поддержку потоковых ответов, кэширование, преобразование и сжатие тела, а также ограничения долгих соединений. Контрольный обход CDN выполняйте только из доверенной среды и не публикуйте origin-адрес.

Результат удобно фиксировать так:

| Где наблюдается пачка | Вероятный участок | | --- | --- | | Уже на прямом upstream | приложение или его runtime | | Upstream ровный, локальный nginx задерживает | конфигурация nginx | | nginx ровный, публичный домен задерживает | внешний прокси, WAF или CDN | | Публичный curl -N ровный, интерфейс обновляется пачкой | обработчик или отрисовка в браузере |

Не перепутайте буферизацию с таймаутом

Буферизация задерживает данные, а таймаут разрывает молчащее соединение. У nginx proxy_read_timeout относится к паузе между последовательными чтениями от upstream, а не к общей длительности SSE-запроса.

Если события могут долго не появляться, сервер может периодически отправлять комментарий SSE вида : keepalive с пустой строкой после него. Браузер не создаст пользовательское событие из такого комментария, но по цепочке пройдут байты. Интервал выбирайте по самому строгому известному idle timeout; heartbeat не заменяет отключение буферизации.

Проверьте исправление до полного включения

Проведите тест на ограниченной группе запросов и подтвердите четыре вещи:

  • события приходят по одному и в правильном порядке;
  • соединение переживает обычную паузу между событиями;
  • после краткого обрыва клиент корректно переподключается;
  • изменение не создало лишнюю нагрузку по памяти и числу открытых соединений.

Web-Puls помогает регулярно проверять доступность публичного сайта, но причину задержки внутри SSE-цепочки нужно искать послойным сравнением. Если у вас несколько прокси и безопасно проверить цепочку самостоятельно не получается, отправьте заявку на профессиональную диагностику: укажите публичный URL, время и часовой пояс проверки, ожидаемый интервал событий, схему прокси и обезличенные результаты тестов.

Проверьте свой сайт прямо сейчас

Введите адрес сайта: Web-Puls покажет HTTP-код, время ответа и базовую диагностику. Для постоянного контроля можно подключить мониторинг.

Нужна помощь с диагностикой ошибки?

Опишите симптомы в короткой заявке: URL, код ответа, время появления и что менялось перед сбоем. Специалист поддержки оценит задачу и предложит формат работ.