# Не позволяйте Django напрямую отдавать файлы: повышаем производительность загрузки с помощью X-Accel-Redirect Большинство сервисов Django используют `FileResponse` для отдачи защищённых файлов (только авторизованным пользователям, после оплаты и т.д.). При небольшом объёме трафика и внутренней коммуникации такой подход вполне достаточен. Но когда запросы на файлы резко возрастают, приложение начинает «залипать» в процессе отдачи. **Python‑сервер** вынужден заниматься чтением файлов, а значит, не успевает выполнять остальные задачи (проверка прав, бизнес‑логика, обработка API). В таких случаях удобно делегировать передачу файлов **Nginx**, оставив Django только за проверкой прав. Это и есть принцип X‑Accel‑Redirect. --- ## Почему прямой вывод файлов в Python приводит к узким местам? {#sec-df5683b425c0} Типичный поток: 1. Приём запроса 2. Проверка прав 3. Чтение файла с диска/хранилища 4. Передача данных через приложение (streaming) Проблема в шагах 3‑4: они «тяжёлые». * Чем больше файл, тем дольше его передача * При росте одновременных загрузок воркеры/потоки блокируются * В итоге увеличивается время ответа API, появляются тайм‑ауты и нагрузка на сервер Nginx, напротив, оптимизирован под статическую отдачу: `sendfile`, эффективный цикл событий, буферизация и поддержка диапазонных запросов. --- ## Суть X‑Accel‑Redirect {#sec-8f39639ab234} **Django отвечает только за проверку, Nginx — за передачу.** ### Как это работает {#sec-acfe1fded112} 1. Клиент запрашивает `/download/123` 2. Django делает только запрос к БД и проверку прав 3. Django возвращает пустое тело с заголовком: ``` X-Accel-Redirect: /_protected/real/path/to/file.webp ``` 4. Nginx видит этот заголовок, ищет файл по внутреннему пути и отдаёт его клиенту напрямую Таким образом Django не читает файл, а лишь подтверждает право доступа. --- ## Когда стоит использовать X‑Accel‑Redirect? {#sec-fc58c3d25d4f} ### 1) Высокая частота и параллельность запросов {#sec-00164dd4ae34} * Сообщества, мессенджеры, отчёты PDF * Паттерн «много запросов, простая логика» — X‑Accel‑Redirect блестит ### 2) Большие файлы и важность диапазонных запросов {#sec-e39fd6155745} * Видео, аудио, архивы * Браузер/плеер запрашивает `Range` → Nginx обрабатывает это надёжно ### 3) Снижение стоимости приложения {#sec-c09b34610e6f} * Python‑воркеры дорогие (память/CPU) * Перенос передачи файлов в прокси‑слой освобождает ресурсы --- ## Когда можно обойтись без X‑Accel‑Redirect? {#sec-1357fdc58f72} * Внутренние сервисы, низкая нагрузка * Мало запросов, логика в API является узким местом * Файлы находятся в S3 и уже обслуживаются CDN/пресайн‑URL В этих случаях `FileResponse` вполне подходит. --- ## Пример реализации: Django + Nginx {#sec-7b7a69572111} ![Поток обработки веб‑запроса](/media/editor_temp/6/a1ad0374-979a-447e-b586-81ac02f2b447.png) ### Конфигурация Nginx {#sec-f9441826ea70} Ключевой директива — `internal`. Путь, помеченный как `internal`, недоступен напрямую из браузера, но может быть использован через X‑Accel‑Redirect. ```nginx # Внутренний эндпоинт для защищённых файлов 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 {#sec-0bf0a74802d0} ```python 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 безопасности {#sec-2fdde1f8ddfe} ### 1) Внутренний путь определяется сервером {#sec-267595d99eac} * Не допускайте, чтобы пользователь мог задать путь вида `/_protected/../../etc/passwd` * Храните только безопасные относительные пути в БД или используйте белый список ### 2) `internal` в Nginx обязательно {#sec-16c9962b5114} * Без `internal` любой может напрямую обратиться к `/ _protected/...` ### 3) Проверка прав только в Django {#sec-019d34af62f6} * Nginx выступает только как транспортный механизм --- ## Альтернативы через сторонние сервисы {#sec-735de93cc272} Если бюджет позволяет, можно отдавать файлы напрямую из облачного хранилища. * **CDN‑кеш**: для публичных файлов CDN обычно эффективнее, чем Nginx * **Пресайн‑URL (S3 и др.)**: проще, чем X‑Accel‑Redirect, если файлы уже находятся в облаке --- ## Итоги {#sec-d2260ecd846a} В итоге отдача файлов через Nginx с X‑Accel‑Redirect даёт заметный прирост производительности и снижает нагрузку на приложение. Это особенно важно при больших объёмах и высокой параллельности запросов. Если трафик невелик, `FileResponse` остаётся простым и надёжным решением. Запомните: **«Права проверяет Django, отдаёт Nginx»**.