# No dejes que Django entregue los archivos directamente: mejora el rendimiento de descargas con X-Accel-Redirect La mayoría de las aplicaciones Django sirven archivos protegidos (solo usuarios autenticados, pagos previos, etc.) mediante `FileResponse`, lo que implica que el proceso Python lee el archivo y lo envía al cliente. Si el tráfico es bajo o la comunicación es interna, esto funciona bien. Pero cuando las peticiones de archivos se disparan, el servidor de aplicación se queda atascado en la entrega de archivos, dificultando la ejecución de la lógica de negocio, la verificación de permisos y la respuesta a la API. En estos casos, delegar la entrega de archivos a Nginx mediante **X-Accel-Redirect** es la solución. --- ## ¿Por qué enviar archivos directamente desde Python crea cuellos de botella? {#sec-df5683b425c0} El flujo típico de Django al servir un archivo es: 1. Recibe la petición 2. Verifica permisos 3. Lee el archivo desde disco/almacenamiento 4. Envía el contenido al cliente (streaming) Los pasos 3 y 4 son los que más peso tienen: * Archivos grandes tardan más en transmitirse * Con más descargas simultáneas, los workers/threads/procesos se bloquean * Se generan retrasos en la API, timeouts y presión para escalar el servidor Nginx, por otro lado, está optimizado para la entrega de archivos estáticos: `sendfile`, bucles de eventos eficientes, manejo de buffers y peticiones de rango. --- ## Idea central de X-Accel-Redirect {#sec-8f39639ab234} **Django solo verifica, Nginx solo entrega.** ### Funcionamiento {#sec-acfe1fded112} 1. El cliente solicita `/download/123` 2. Django realiza la consulta a la BD y la verificación de permisos 3. Django devuelve una respuesta con cabecera: ``` X-Accel-Redirect: /_protected/real/path/to/file.webp ``` y cuerpo vacío 4. Nginx detecta la cabecera y busca el archivo internamente, enviándolo directamente al cliente Así, Django no lee el contenido del archivo; solo decide si el usuario puede acceder. --- ## Cuándo es especialmente útil {#sec-fc58c3d25d4f} ### 1) Servicios con muchas descargas o imágenes y alta concurrencia {#sec-00164dd4ae34} * Comunidades, mensajería, archivos adjuntos, PDFs de reportes * Patrones donde la lógica es simple y la cantidad de peticiones es alta ### 2) Archivos grandes o peticiones de rango importantes {#sec-e39fd6155745} * Vídeos, audios, archivos comprimidos de gran tamaño * Navegadores o reproductores que usan `Range` para reproducir o continuar la descarga ### 3) Reducir costos del servidor de aplicación {#sec-c09b34610e6f} * Los workers de Python son costosos (memoria/CPU). Si se encargan de la entrega, el gasto aumenta * Delegar a Nginx permite que el servidor de aplicación se concentre en la lógica --- ## Cuando no es necesario {#sec-1357fdc58f72} * Comunicación interna con tráfico bajo * Pocas peticiones de archivos; la lógica de la API es el cuello de botella * Archivos almacenados en S3 u otro objeto externo con CDN o URLs prefirmadas En estos casos, `FileResponse` sigue siendo suficiente. --- ## Ejemplo de implementación: Django + Nginx {#sec-7b7a69572111} ![Flujo de procesamiento de peticiones web](/media/editor_temp/6/a1ad0374-979a-447e-b586-81ac02f2b447.png) ### Configuración de Nginx {#sec-f9441826ea70} El punto clave es `internal`. Una ubicación marcada como `internal` no puede ser accedida directamente por el cliente; solo por redirecciones internas como X-Accel-Redirect. ```nginx # Endpoint interno que sirve archivos protegidos location /_protected/ { internal; # Directorio donde están los archivos reales alias /var/app/protected_media/; # Opciones de rendimiento (ajusta según tu entorno) sendfile on; tcp_nopush on; # Opcional: control de caché/headers # add_header Cache-Control "private, max-age=0"; } ``` * Se asume que los archivos reales están bajo `/var/app/protected_media/` * La URL pública es `/download/...` manejada por Django * La ruta interna es `/_protected/...` --- ### Vista de 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) Consulta a la BD + verificación de permisos obj = get_file_object_or_404(file_id) # ejemplo if not obj.can_download(request.user): raise Http404 # 2) Construye la ruta interna (mapeada a /_protected/) internal_path = f"/_protected/{obj.storage_relpath}" # 3) Configura la cabecera X-Accel-Redirect y deja el cuerpo vacío response = HttpResponse() response["X-Accel-Redirect"] = iri_to_uri(internal_path) # Opcional: nombre de descarga y tipo MIME response["Content-Type"] = obj.content_type or "application/octet-stream" response["Content-Disposition"] = f'attachment; filename="{obj.download_name}"' return response ``` Puntos clave: * No hay `FileResponse(open(...))` ni I/O de archivos * El tiempo de respuesta de Django es mínimo; los workers no se bloquean --- ## Checklist de seguridad {#sec-2fdde1f8ddfe} ### 1) La ruta interna debe ser decidida por el servidor {#sec-267595d99eac} * Evita que el cliente construya rutas como `/_protected/../../etc/passwd` * Usa rutas relativas seguras almacenadas en la BD o un mapeo basado en whitelist ### 2) La ubicación de Nginx debe ser `internal` {#sec-16c9962b5114} * Sin `internal`, un usuario podría acceder directamente a `/_protected/...` ### 3) La lógica de permisos debe confiar únicamente en Django {#sec-019d34af62f6} * Nginx solo es el motor de entrega; la autorización la gestiona Django --- ## Alternativas con servicios de terceros {#sec-735de93cc272} Si el presupuesto lo permite, se puede servir directamente desde un almacenamiento externo. Esto ahorra recursos del servidor y suele ser más fiable. * **CDN**: para archivos públicos, un CDN antes de Nginx aporta mayor rendimiento * **URLs prefirmadas (S3, etc.)**: si el objeto está en S3, usar URLs prefirmadas puede ser más sencillo que X-Accel-Redirect --- ## Conclusión {#sec-d2260ecd846a} En resumen, delegar la entrega de archivos a Nginx mediante X-Accel-Redirect mejora notablemente el rendimiento y la escalabilidad de las aplicaciones Django. Nginx, optimizado para archivos estáticos, maneja la transmisión de manera eficiente, mientras que Django se centra en la lógica de negocio y la autorización. Si el tráfico no es intenso, `FileResponse` sigue siendo una opción limpia; pero cuando las descargas se disparan, X-Accel-Redirect es la solución más rápida y efectiva. Recuerda: **“Permisos en Django, entrega en Nginx.”**