Top.Mail.Ru
OSEN-НИЙ SAAALEСкидка 50% на виртуальный хостинг и VDS
до 30.11.2025 Подробнее
Выберите продукт

Range‑запросы без сюрпризов: частичная отдача файлов в Nginx/Apache и кэш

Раздаёте видео, большие архивы или аудио? Запросы с Range и ответы 206 — норма. Показываю, как настроить Nginx и Apache: заголовки, кэш (slice/mod_cache), валидаторы, компрессию и тесты — чтобы частичная отдача была стабильной под нагрузкой.
Range‑запросы без сюрпризов: частичная отдача файлов в Nginx/Apache и кэш

Когда сайт начинает отдавать крупные файлы — видео, аудио, образы, большие архивы — запросы с заголовком Range появляются немедленно. Их присылают медиаплееры для перемотки, скачивальщики для докачки и браузеры при нестабильных сетях. Ответ на такой запрос — 206 Partial Content — кажется простым, но на практике часто бьётся о кэш, компрессию, ETag и прокси. Ниже — практический минимум по Nginx и Apache, чтобы частичная отдача работала стабильно и предсказуемо.

Что такое HTTP Range и когда он нужен

Range: bytes=... просит сервер выдать не весь ресурс, а часть. Клиент указывает диапазон байт: от‑до или от‑конца. Сервер отвечает 206 Partial Content с заголовком Content-Range и куском тела. Зачем это нужно:

  • Перемотка в медиаплеере без скачивания всего файла.
  • Докачка после обрыва — клиент запрашивает недостающий хвост.
  • Оптимизация доставки через кэш: можно отдать только востребованный фрагмент.

Сервер, который умеет корректно Range, обычно добавляет Accept-Ranges: bytes в ответы 200/206, тем самым сигнализируя клиентам поддержку частичной отдачи.

Ключевые заголовки и их смысл

  • Range: bytes=0-1023 — запрос первых 1024 байт. Можно просить «с конца»: bytes=-1024.
  • Content-Range: bytes 0-1023/1234567 — что именно отдали: диапазон и полный размер.
  • Accept-Ranges: bytes — сервер поддерживает отдачу по байтам.
  • If-Range — условная частичная отдача: «если контент не изменился (по ETag или Last-Modified), верни 206; иначе отдай целиком 200».
  • ETag и Last-Modified — валидаторы. Для If-Range корректно работает только сильный ETag (без префикса W/).

Правило безопасности: если запрошенный диапазон некорректен (выходит за размер файла), возвращайте 416 Range Not Satisfiable с Content-Range: bytes */size. Это помогает клиентам пересчитать офсеты.

Частичная отдача и кэш: почему возникают проблемы

Кэширование 206 — тонкая тема. Базовые прокси и браузеры не всегда кэшируют частичные ответы, а некоторые серверы по умолчанию не хотят класть 206 в кэш. Отсюда типовые симптомы:

  • Множественные запросы к бэкенду при перемотке — каждый «скачок» лезет на origin.
  • Несовместимость с компрессией: диапазон относится к байтам сжатого тела, а не исходного контента, что ломает воспроизведение.
  • Конфликт валидаторов между нодами: разные ETag вызывают 200 вместо 206, сбивая клиентов.

Есть две рабочие стратегии:

  1. Игнорировать Range на уровне бэкенда и кэшировать полный 200, а частичные ответы отдавать уже из кэша. Так умеет Apache mod_cache (директива CacheIgnoreRange), и так можно организовать в Nginx через модуль slice.
  2. Кэшировать фрагменты как отдельные сущности («шардировать» ресурс на куски фиксированного размера) и собирать ответ из этих кусочков. Это классический подход Nginx с slice для медиаконтента.

Если вы выбираете стратегию кэширования и политику валидаторов, пригодится материал Cache-Control и ETag для статики.

Как запрос с Range проходит через прокси и кэш с шардированием

Nginx: статическая отдача с Range без сюрпризов

Nginx из коробки корректно поддерживает Range для статических файлов. Основная задача — сделать отдачу неблокирующей и ограничить злоупотребления многодиапазонными запросами.

# /etc/nginx/nginx.conf (фрагмент)
http {
    # Включаем сильные ETag для статического контента
    etag on;

    # Ограничение многодиапазонных запросов (защита от лишней нагрузки)
    max_ranges 1;
}
# server/location для больших файлов
location /media/ {
    # Неблокирующая отдача больших файлов
    sendfile on;
    tcp_nopush on;
    aio threads;
    directio 8m;

    # Явно показываем клиентам поддержку Range
    add_header Accept-Ranges bytes;

    # Контроль кэшируемости (пример, подбирайте значения под проект)
    add_header Cache-Control public, max-age=31536000, immutable;
}

Зачем directio и aio threads? Для больших объектов это снижает блокировки на диске: чтение идёт в обход page cache (с выбранного порога) и выполняется пулом потоков, а не рабочими процессами Nginx. max_ranges 1 закрывает старый класс атак и странные кейсы мультидиапазонов, которые большинству медиаплееров не нужны. Если вы только выбираете стек обслуживания статики, посмотрите короткое сравнение Nginx vs Apache.

Проверяем статическую отдачу

# Полный ответ (ожидаем 200 и Accept-Ranges)
curl -I https://example.org/media/big.mp4

# Первый килобайт (ожидаем 206 и Content-Range)
curl -i -H "Range: bytes=0-1023" https://example.org/media/big.mp4

# Хвост с произвольной позиции
curl -i -H "Range: bytes=1048576-" https://example.org/media/big.mp4

Проверьте, что Content-Length в 200‑ответе равен полному размеру, а Content-Range в 206 корректно указывает диапазон и общий размер.

Nginx как прокси: кэш и Range через slice

Если файлы лежат за бэкендом (S3‑совместимое хранилище, приложение, другой сервер) и вы хотите эффективно кэшировать частичные запросы, удобен модуль slice. Он «разрезает» ресурс на фиксированные куски и может кэшировать каждый слайс отдельно.

# Общие настройки кэша
proxy_cache_path /var/cache/nginx/media levels=1:2 keys_zone=media:20m max_size=50g inactive=7d;

server {
    location /video/ {
        # Размер шардирования (подбирайте под типичный bitrate и RTT)
        slice 1m;

        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";

        # Пробрасываем диапазон слайса к origin
        proxy_set_header Range $slice_range;

        # Ключ кэша должен включать диапазон
        proxy_cache media;
        proxy_cache_key $scheme$proxy_host$request_uri$slice_range;
        proxy_cache_valid 200 206 30m;

        # Если origin не умеет Range и отдаёт 200, Nginx сам сформирует 206
        proxy_force_ranges on;

        # Защита и предсказуемость
        max_ranges 1;
        add_header Accept-Ranges bytes;

        # Рекомендованная кэшируемость для медиа
        add_header Cache-Control public, max-age=604800;
    }
}

Как это работает: клиент просит bytes=10m-, Nginx разбивает на сегменты по 1 МБ, делает несколько подзапросов с Range к origin, складывает слайсы в кэш с разными ключами и собирает итоговый 206 для клиента. Повторные перемотки для соседних диапазонов чаще попадут в кэш.

Важно учитывать компрессию: если gzip включён для типа контента, диапазон будет относиться к сжатому телу. Для видео/аудио сжатие обычно не нужно — исключите медиа из gzip_types, иначе Range станет бесполезным для плееров.

Для высокой нагрузки и контроля кеша выделяйте отдельный узел — на собственном VDS можно гибко настроить Nginx, диски и лимиты под профиль медиатрафика.

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

Типичные ловушки с Nginx и Range

  • Разные ETag на нодах. Для статических файлов оставляйте etag on, но следите, чтобы при выкладке сохранялись время модификации и размер — тогда ETag совпадёт. Синхронизируйте контент так, чтобы сохранялся mtime.
  • Многодиапазонные запросы. Ограничьте max_ranges 1. Этого достаточно для большинства клиентов и экономит CPU/IO.
  • Кэш и 206. Не все прослойки охотно кэшируют 206. С slice складывайте куски как 200/206 и собирайте ответ. Без шардирования кэшировать произвольные диапазоны не стоит — получится фрагментация.
  • Несовместимость с gzip для медиа. Уберите медиа из gzip_types, чтобы диапазоны соответствовали реальным байтам файла.

Концепция настроек Nginx и доставки медиа с Range

Apache: статическая выдача Range и ограничение мультидиапазонов

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

# httpd.conf или vhost (фрагмент)
# Сильные ETag без inode (стабильно на кластере)
FileETag MTime Size

# Лимитируем количество диапазонов в запросе
MaxRanges 1

# Рекомендации для статической отдачи
EnableSendfile On
EnableMMAP Off
# Пример контекстных настроек для каталога с медиа
<Directory "/var/www/media">
    # Явно объявляем поддержку Range
    Header set Accept-Ranges bytes

    # Делаем контент публично кэшируемым (подберите сроки хранения)
    Header set Cache-Control "public, max-age=604800"
</Directory>

FileETag MTime Size — важный момент для кластеров: исключаем инод, чтобы ETag был одинаковым на разных серверах при равных размере и времени модификации.

Apache mod_cache: игнорировать Range на backend, отдавать из кэша

Если хотите, чтобы backend всегда получал запросы без Range, а частичные ответы обслуживались фронтендом из кэша, используйте mod_cache с директивой CacheIgnoreRange:

# Диск-кэш для /media, игнорируем Range при запросе к origin
CacheQuickHandler On
CacheLock On
CacheLockAge 5
CacheLockMaxAge 10

CacheEnable disk "/media"
CacheIgnoreRange On
CacheHeader on

Схема такая: первый запрос с Range приведёт к получению полного объекта 200 от origin и его кэшированию; затем Apache сам нарежет 206 для клиентов из локального кэша. Этот подход хорош, если объект имеет разумный размер и часто переиспользуется.

Валидаторы, If-Range и консистентность

If-Range — полезный механизм: клиент запрашивает часть файла только если он не изменился. Если валидатор не совпал, сервер вернёт полный 200. Чтобы всё работало предсказуемо:

  • Обеспечьте сильный ETag (без W/) и стабильный между нодами. В Nginx для статических файлов это достигается сохранением mtime при выкладке. В Apache используйте FileETag MTime Size.
  • Не меняйте ETag по пустякам. Например, не пересобирайте файл «на месте» без изменения содержимого — ETag должен отражать контент.
  • Для длинного хранения в кэше настраивайте Cache-Control и, при необходимости, Vary (например, по Accept-Encoding), чтобы избежать путаницы между сжатыми и несжатыми вариантами.

Важно: диапазоны относятся к конкретному представлению ресурса. Если выдаёте gzipped-версию, диапазон указывает байты сжатого потока. Для медиа обычно избегайте компрессии.

Проверка сценариев с curl

Ниже минимальный набор проверок, который я делаю после настройки:

# 1) Базовая проверка 206
curl -i -H "Range: bytes=0-1023" https://example.org/media/big.mp4

# 2) Диапазон с открытым концом
curl -i -H "Range: bytes=1048576-" https://example.org/media/big.mp4

# 3) Некорректный диапазон (должен быть 416)
curl -i -H "Range: bytes=9999999999-" https://example.org/media/big.mp4

# 4) If-Range с ETag (подставьте реальный ETag)
curl -i -H "If-Range: \"123456789abcdef\"" -H "Range: bytes=0-1023" https://example.org/media/big.mp4

# 5) Повторный запрос — проверяем кэш-хиты (по server headers)
curl -i -H "Range: bytes=0-1048575" https://example.org/video/film.mp4

Проверьте, что первый запрос к проксируемой локации собрался из слайсов и последующие приходят из кэша (по диагностическим заголовкам, которые вы добавляете сами).

Чеклист для продакшена

  • Nginx статик: etag on, sendfile on, aio threads, порог directio, max_ranges 1, корректные Cache-Control.
  • Nginx прокси: slice, proxy_cache_key с $slice_range, proxy_set_header Range $slice_range, proxy_force_ranges on, ограничение max_ranges.
  • Apache статик: EnableSendfile On, EnableMMAP Off, MaxRanges 1, FileETag MTime Size, заголовок Accept-Ranges.
  • Apache mod_cache: CacheIgnoreRange On для стратегии «кэшируем полный объект».
  • Компрессия: не применять gzip к аудио/видео; аккуратно с Range и сжатием.
  • Валидаторы: сильные ETag и консистентность между нодами; сохранение mtime при выкладке.
  • Тесты: 200/206/416, If-Range, кэш‑хиты, поведение при перемотке.

Практические советы по выбору размеров и таймаутов

  • Размер слайса: начните с 1–4 МБ. Меньше — больше накладных расходов на запросы к origin, больше — хуже «попадание» при перемотке.
  • Кэш: под медиакаталог выделите отдельную keys_zone и лимит по размеру; следите за inactive, чтобы «редкие» куски не вываливались слишком быстро.
  • Таймауты: для origin увеличьте proxy_read_timeout и send_timeout, если сеть до хранилища неидеальна. Отсечка по соединениям клиента — по профилю нагрузки.
  • Грейс: если используете прокси‑кэш с выдачей «устаревшего при ошибке», добавляйте настроенный «stale» для устойчивости к всплескам ошибок origin.

Что сломается, если сделать «не так»

  • Медиаплеер зависает при перемотке — вероятно, получаете сжатое тело и диапазон относится к gz‑потоку, а не к байтам файла.
  • Постоянные 200 вместо 206 при If-Range — валидаторы различаются между нодами, ETag слабый или меняется без изменения содержимого.
  • Бурст запросов на origin при пиковом онлайне — частичные ответы не кэшируются; внедряйте slice или стратегию полного кэша с игнором Range на backend.
  • Неожиданные 416 — клиент просит диапазоны вне размера. Проверьте корректность Content-Length на 200 и Content-Range на 206.

Итоги

Частичная отдача файлов — обязаловка для любого медиапроекта и просто крупных загрузок. Чтобы HTTP Range и 206 Partial Content работали без сюрпризов, настройте сервер и кэш согласованно: включите предсказуемые валидаторы, ограничьте мультидиапазоны, продумайте стратегию кэширования (полные объекты либо шардирование с slice), аккуратно обойдитесь с компрессией. После базовой доводки вы получите экономию трафика к origin, стабильную перемотку и отсутствие «битых» докачек — ровно то, что нужно медиа‑сайтам и серверам скачиваний.

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

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

Nginx SSI и подзапросы: сборка страниц из блоков с кэшированием OpenAI Статья написана AI Fastfox

Nginx SSI и подзапросы: сборка страниц из блоков с кэшированием

Практическое руководство по Nginx SSI и subrequest: сборка страницы из блоков, фрагментное кэширование, разделение гостей и автори ...
Мониторинг OPcache: метрики, алерты и быстрая диагностика утечек OpenAI Статья написана AI Fastfox

Мониторинг OPcache: метрики, алерты и быстрая диагностика утечек

OPcache ускоряет PHP, но под нагрузкой может «захлебнуться»: заканчивается память, растёт фрагментация, падает hit rate. Разбираем ...
rclone для больших файлов: multipart‑загрузки, параллелизм и контроль памяти OpenAI Статья написана AI Fastfox

rclone для больших файлов: multipart‑загрузки, параллелизм и контроль памяти

Большие файлы и S3 требуют точной настройки rclone: multipart‑загрузка, параллелизм потоков, контроль памяти и полосы, устойчивост ...