Не позволяйте Django напрямую отдавать файлы: повышаем производительность загрузки с помощью X-Accel-Redirect
Большинство сервисов Django используют FileResponse для отдачи защищённых файлов (только авторизованным пользователям, после оплаты и т.д.). При небольшом объёме трафика и внутренней коммуникации такой подход вполне достаточен.
Но когда запросы на файлы резко возрастают, приложение начинает «залипать» в процессе отдачи. Python‑сервер вынужден заниматься чтением файлов, а значит, не успевает выполнять остальные задачи (проверка прав, бизнес‑логика, обработка API). В таких случаях удобно делегировать передачу файлов Nginx, оставив Django только за проверкой прав. Это и есть принцип X‑Accel‑Redirect.
Почему прямой вывод файлов в Python приводит к узким местам?
Типичный поток:
- Приём запроса
- Проверка прав
- Чтение файла с диска/хранилища
- Передача данных через приложение (streaming)
Проблема в шагах 3‑4: они «тяжёлые».
- Чем больше файл, тем дольше его передача
- При росте одновременных загрузок воркеры/потоки блокируются
- В итоге увеличивается время ответа API, появляются тайм‑ауты и нагрузка на сервер
Nginx, напротив, оптимизирован под статическую отдачу: sendfile, эффективный цикл событий, буферизация и поддержка диапазонных запросов.
Суть X‑Accel‑Redirect
Django отвечает только за проверку, Nginx — за передачу.
Как это работает
- Клиент запрашивает
/download/123 - Django делает только запрос к БД и проверку прав
- Django возвращает пустое тело с заголовком:
X-Accel-Redirect: /_protected/real/path/to/file.webp - Nginx видит этот заголовок, ищет файл по внутреннему пути и отдаёт его клиенту напрямую
Таким образом Django не читает файл, а лишь подтверждает право доступа.
Когда стоит использовать X‑Accel‑Redirect?
1) Высокая частота и параллельность запросов
- Сообщества, мессенджеры, отчёты PDF
- Паттерн «много запросов, простая логика» — X‑Accel‑Redirect блестит
2) Большие файлы и важность диапазонных запросов
- Видео, аудио, архивы
- Браузер/плеер запрашивает
Range→ Nginx обрабатывает это надёжно
3) Снижение стоимости приложения
- Python‑воркеры дорогие (память/CPU)
- Перенос передачи файлов в прокси‑слой освобождает ресурсы
Когда можно обойтись без X‑Accel‑Redirect?
- Внутренние сервисы, низкая нагрузка
- Мало запросов, логика в API является узким местом
- Файлы находятся в S3 и уже обслуживаются CDN/пресайн‑URL
В этих случаях FileResponse вполне подходит.
Пример реализации: Django + Nginx

Конфигурация Nginx
Ключевой директива — internal. Путь, помеченный как internal, недоступен напрямую из браузера, но может быть использован через X‑Accel‑Redirect.
# Внутренний эндпоинт для защищённых файлов
location /_protected/ {
internal;
# Физический путь к файлам
alias /var/app/protected_media/;
# Оптимизация
sendfile on;
tcp_nopush on;
# При необходимости можно задать заголовки кэширования
# add_header Cache-Control "private, max-age=0";
}
- Предполагается, что файлы находятся в
/var/app/protected_media/ - Внешний URL остаётся
/download/...(обрабатывается Django) - Внутренний путь всегда
/ _protected/...
Пример представления Django
from django.http import HttpResponse, Http404
from django.contrib.auth.decorators import login_required
from django.utils.encoding import iri_to_uri
@login_required
def download(request, file_id):
# 1) Получаем объект и проверяем права
obj = get_file_object_or_404(file_id)
if not obj.can_download(request.user):
raise Http404
# 2) Формируем внутренний путь
internal_path = f"/_protected/{obj.storage_relpath}"
# 3) Возвращаем ответ только с заголовком
response = HttpResponse()
response["X-Accel-Redirect"] = iri_to_uri(internal_path)
# (опционально) имя файла и тип контента
response["Content-Type"] = obj.content_type or "application/octet-stream"
response["Content-Disposition"] = f'attachment; filename="{obj.download_name}"'
return response
- Нет чтения файла через
FileResponse - Время обработки запроса минимально, воркеры не блокируются
Checklist безопасности
1) Внутренний путь определяется сервером
- Не допускайте, чтобы пользователь мог задать путь вида
/_protected/../../etc/passwd - Храните только безопасные относительные пути в БД или используйте белый список
2) internal в Nginx обязательно
- Без
internalлюбой может напрямую обратиться к/ _protected/...
3) Проверка прав только в Django
- Nginx выступает только как транспортный механизм
Альтернативы через сторонние сервисы
Если бюджет позволяет, можно отдавать файлы напрямую из облачного хранилища.
- CDN‑кеш: для публичных файлов CDN обычно эффективнее, чем Nginx
- Пресайн‑URL (S3 и др.): проще, чем X‑Accel‑Redirect, если файлы уже находятся в облаке
Итоги
В итоге отдача файлов через Nginx с X‑Accel‑Redirect даёт заметный прирост производительности и снижает нагрузку на приложение. Это особенно важно при больших объёмах и высокой параллельности запросов. Если трафик невелик, FileResponse остаётся простым и надёжным решением.
Запомните: «Права проверяет Django, отдаёт Nginx».