Акция Панель управления ispmanager для VDS — первый месяц бесплатно
до 31.07.2026 Подробнее
Выберите продукт

Nginx Range Requests и 206 Partial Content: Accept-Ranges, буферизация и стриминг

Range-запросы в Nginx нужны для докачки и перемотки видео: клиент запрашивает диапазоны байтов и получает 206 Partial Content. Разберём Accept-Ranges, влияние proxy_buffering и причины, почему вместо 206 приходит 200, а также проверки через curl и access-логи.
Nginx Range Requests и 206 Partial Content: Accept-Ranges, буферизация и стриминг

Range-запросы (HTTP Range) позволяют клиенту запросить не весь ресурс целиком, а конкретный диапазон байтов (byte ranges). В ответ сервер отдаёт 206 Partial Content и заголовок Content-Range. В реальной эксплуатации это критично для докачки после обрыва, параллельных загрузок и перемотки видео в браузере.

Проблемы вокруг Range обычно выглядят одинаково: нет Accept-Ranges, вместо 206 приходит 200, MP4 не перематывается через reverse proxy, а включение/выключение proxy_buffering даёт «магические» эффекты. Ниже — практический разбор, как это работает в Nginx и как быстро локализовать слой, где Range «теряется».

Как выглядит Range-обмен: 206 Partial Content и ключевые заголовки

Классический пример: клиент запрашивает первые 1 МБ файла.

curl -I -H "Range: bytes=0-1048575" http://example.com/bigfile.bin

Ожидаемые признаки корректной частичной выдачи:

  • статус: HTTP/1.1 206 Partial Content;
  • Content-Range: bytes 0-1048575/123456789;
  • Content-Length равен размеру диапазона;
  • Accept-Ranges: bytes (часто есть на 200 и нередко на 206).

Если сервер не поддерживает диапазоны, он обычно отвечает 200 OK с полным телом. Если диапазон некорректный — будет 416 Range Not Satisfiable.

Наличие Accept-Ranges: bytes — полезный сигнал. Но реальная проверка поддержки Range делается запросом с Range и ожиданием ответа 206 с Content-Range.

Что в Nginx реально отвечает за byte ranges

Для статических файлов Nginx поддерживает Range «из коробки». Но итоговое поведение зависит от того, где именно находится контент: это файл на диске, ответ upstream-приложения, объект из кеша или результат фильтров (gzip и т. п.). Поэтому для диагностики важно понимать, каким путём Nginx получил тело ответа.

Статика: обычно всё работает сразу

Если вы отдаёте файл через location с root/alias, в большинстве случаев Range будет работать автоматически.

server {
  listen 80;
  server_name example.com;

  location /download/ {
    root /var/www/site;
  }
}

Проверка:

curl -I http://example.com/download/iso/test.iso
curl -I -H "Range: bytes=0-1" http://example.com/download/iso/test.iso

MP4 streaming и «перемотка»: почему важен Range

Для воспроизведения MP4 в браузере клиент почти всегда использует Range. Если перемотка не работает, типичный симптом — на Range-запрос приходит 200 (полная отдача), либо по пути теряются заголовки Content-Range/Accept-Ranges.

Отдельный нюанс: «быстрая перемотка» зависит от расположения атома moov. Если moov в конце файла, браузер сначала запрашивает «хвост» через Range. Поэтому иногда проблема не в Nginx, а в упаковке самого файла (нужен faststart при подготовке).

Если вы размещаете медиа на отдельной машине, часто удобнее выносить Nginx под такие задачи на отдельный сервер или VDS, чтобы не конкурировать по диску и IO с приложением и базой.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Почему Range ломается: типовые причины и быстрые проверки

Пример ответа 206 Partial Content и заголовка Content-Range в выводе curl

1) Между клиентом и вашим Nginx есть ещё один прокси

Внешний прокси/CDN может не пробрасывать Range, агрессивно кешировать и возвращать 200 из кеша или переписывать заголовки так, что Content-Range пропадает. Первое, что нужно сделать на своей стороне — убедиться, что заголовок Range действительно доходит до Nginx.

Удобный способ — временно добавить расширенный формат логов и посмотреть одновременно входящий $http_range, то, что отправил Nginx, и (если это proxy) то, что ответил upstream.

log_format range_dbg '$remote_addr - $host [$time_local] "$request" $status '
                     'range="$http_range" sent_range="$sent_http_content_range" '
                     'accept_ranges="$sent_http_accept_ranges" '
                     'up_status="$upstream_status" up_range="$upstream_http_content_range"';

access_log /var/log/nginx/access_range.log range_dbg;

После reload сделайте тест:

nginx -t
nginx -s reload
curl -I -H "Range: bytes=0-1" http://example.com/video.mp4

В логе вас интересуют поля range=, sent_range= и up_range=: так вы быстро поймёте, где именно Range «пропал».

2) Nginx — reverse proxy, а upstream не поддерживает Range

Если контент отдаёт приложение (upstream), то Range «заканчивается» там, где заканчивается способность отдать кусок байтов. Если upstream всегда отвечает 200, Nginx не «нарежет» ответ сам по себе (за исключением отдельных архитектур с slice и кешем).

Базовый паттерн проксирования (с явной передачей Range и If-Range):

location /media/ {
  proxy_pass http://app_backend;
  proxy_set_header Host $host;
  proxy_set_header Range $http_range;
  proxy_set_header If-Range $http_if_range;
}

Проверка upstream напрямую (с хоста, где стоит Nginx):

curl -I -H "Range: bytes=0-1" http://127.0.0.1:8080/media/video.mp4

Если upstream не выдаёт 206, лечить нужно там: настройками приложения, хранилища, либо выносом медиа в статику.

3) proxy_buffering не «ломает Range», но ломает ожидания

proxy_buffering часто путают с Range, потому что это влияет на «ощущение» стриминга. В общем случае:

  • proxy_buffering on — Nginx может сначала принимать ответ upstream в буферы (и во временные файлы), а потом отдавать клиенту. Для видео это выглядит как долгий старт или «тупая» перемотка, хотя формально Range может работать.
  • proxy_buffering off — Nginx больше похож на потоковый прокси: быстрее старт, меньше временных файлов, но требования к стабильности upstream выше.

Практичный паттерн для медиа, если upstream сам умеет Range:

location /media/ {
  proxy_pass http://app_backend;
  proxy_set_header Host $host;
  proxy_set_header Range $http_range;
  proxy_set_header If-Range $http_if_range;

  proxy_buffering off;
  proxy_request_buffering off;
}

4) Сжатие и трансформации на лету

Range работает по байтам конкретного представления ресурса. Если на пути включены трансформации (например, gzip на лету), это может конфликтовать с ожиданиями клиента: он просит байты файла, а получает байты сжатого потока. Для бинарников и медиа чаще всего лучше не включать gzip вообще для этих типов.

gzip on;
gzip_types text/plain text/css application/javascript application/json;

Для MP4 и «больших бинарников» обычно достаточно не добавлять соответствующие типы в gzip_types.

Диагностика: как быстро понять, кто виноват — Nginx, upstream или клиент

Шаг 1. Проверяем поведение с Range и без Range

curl -I http://example.com/video.mp4
curl -I -H "Range: bytes=0-1" http://example.com/video.mp4
curl -I -H "Range: bytes=1000000-1000001" http://example.com/video.mp4

Смотрите:

  • есть ли 206 на Range-запрос;
  • корректен ли Content-Range;
  • приходит ли Accept-Ranges;
  • не появляется ли неожиданный Content-Encoding: gzip.

Шаг 2. Сравниваем ответы upstream напрямую и через Nginx

curl -I -H "Range: bytes=0-1" http://127.0.0.1:8080/video.mp4
curl -I -H "Range: bytes=0-1" http://127.0.0.1/video.mp4

Логика простая:

  • upstream = 200, через Nginx тоже 200 — проблема в приложении/хранилище;
  • upstream = 206, а через Nginx = 200 — ищите в Nginx фильтры, кеш, дополнительные прокси-слои, особенности локации.

Шаг 3. Фиксируем Range в логах Nginx

С форматом range_dbg проверьте, что:

  • range не пустой (заголовок дошёл);
  • sent_range появляется на 206;
  • up_range появляется, если upstream реально отдал диапазон.

Если у вас параллельно включены кеш и slice, полезно также выводить $upstream_cache_status и убедиться, что кеш не подменяет поведение Range.

Близкая тема — как Range взаимодействует с кешированием и прокси в разных веб-серверах. По этой логике удобно держать под рукой заметку: Range-запросы и кеш: грабли в Nginx и Apache.

Если вы планируете включать HTTPS для выдачи медиа, заранее проверьте цепочку сертификатов и настройки: в продакшене обычно проще выпускать и продлевать проверенные SSL-сертификаты (например, GlobalSign) и не тратить время на самодельные сборки.

Практика: конфиг-паттерны для скачивания и видео

FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Отладка Range-запросов по access-логам Nginx: Range и Content-Range

Статические большие файлы: отдача с диска

Для больших файлов обычно включают sendfile и настраивают сетевые опции. Пример базовой конфигурации (часть параметров — в контексте http):

http {
  sendfile on;
  tcp_nopush on;
  tcp_nodelay on;

  server {
    listen 80;
    server_name example.com;

    location /files/ {
      root /srv/www;
      access_log /var/log/nginx/files_access.log;
    }
  }
}

В таком сценарии Range, как правило, «просто работает», а нагрузка на CPU минимальная.

Reverse proxy для видео/медиа: сохраняем Range и уменьшаем задержку

location /video/ {
  proxy_pass http://media_backend;
  proxy_set_header Host $host;
  proxy_set_header Range $http_range;
  proxy_set_header If-Range $http_if_range;

  proxy_buffering off;
  proxy_request_buffering off;

  proxy_buffer_size 16k;
  proxy_buffers 8 32k;
}

Если после этого перемотка всё равно не работает, чаще всего причина одна из двух: upstream не умеет Range или MP4 упакован без faststart (метаданные moov не в начале).

Кеш и Range: почему «просто включить proxy_cache» часто плохая идея

С proxy_cache Range становится «скользкой» темой: кеш обычно ключуется по URL и заголовкам, а Range добавляет ещё одно измерение. При наивном кешировании диапазонов легко получить взрыв количества объектов и странные попадания.

Для более предсказуемой работы используют slice: Nginx режет объект на куски фиксированного размера и кеширует их. Это отдельная архитектурная задача, которую стоит принимать исходя из профиля трафика (видео, сегментированные скачивания, повторные запросы одних и тех же диапазонов).

Чеклист: что проверить, если 206 Partial Content не приходит

  1. Запрос действительно содержит Range: bytes=... (проверьте curl или инструменты браузера).

  2. В access-логе Nginx видно $http_range (значит, заголовок дошёл до Nginx).

  3. Контент отдаётся как статика или upstream реально отвечает диапазонами (206 и Content-Range).

  4. Нет трансформаций на лету (gzip для неподходящих типов, фильтры, переписывания).

  5. Если это видео: файл подготовлен корректно (moov в начале), а прокси не буферизует ответ «через силу».

  6. Если есть кеш: убедитесь, что он не подменяет ответы на Range-запросы и не теряет заголовки.

Итоги

Range-запросы в Nginx — базовая функция, которая для статики обычно работает сразу. Проблемы почти всегда начинаются, когда между клиентом и файлом появляется цепочка прокси, буферизация, кеширование или upstream-приложение без поддержки byte ranges. Рабочая стратегия: подтвердите 206 в «чистом» виде (curl + логирование $http_range/$sent_http_content_range), локализуйте слой, где Range пропадает, и только затем тюнингуйте proxy_buffering и кеш.

Поделиться статьей

Вам будет интересно

Debian/Ubuntu: mount: wrong fs type, bad option, bad superblock — как быстро найти и исправить причину OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: mount: wrong fs type, bad option, bad superblock — как быстро найти и исправить причину

Ошибка mount: wrong fs type, bad option, bad superblock в Debian/Ubuntu может означать и простую опечатку в имени раздела, и пробл ...
Debian/Ubuntu: XFS metadata corruption и emergency read-only — пошаговое восстановление OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: XFS metadata corruption и emergency read-only — пошаговое восстановление

Если XFS-раздел внезапно стал доступен только для чтения, а сервер ушёл в emergency mode, главное — не спешить. Разберём безопасны ...
Debian/Ubuntu: как исправить Failed to fetch при apt update OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: как исправить Failed to fetch при apt update

Ошибка Failed to fetch при apt update в Debian и Ubuntu обычно связана не с самим APT, а с DNS, сетью, зеркалом, прокси, временем ...