Laat Nginx de bestandslevering verzorgen in plaats van Django: X-Accel-Redirect voor betere downloadprestaties
De meeste Django‑services leveren beschermde bestanden (alleen voor ingelogde gebruikers, na betaling, etc.) via FileResponse of een vergelijkbare methode, waarbij de Python‑processen het bestand lezen en naar de cliënt sturen. Dit is voldoende voor kleine hoeveelheden verkeer of interne communicatie.
Wanneer het aantal bestandsverzoeken echter explosief toeneemt, raakt de applicatie‑server (Python) verstrikt in het afleveren van bestanden en kan het moeilijk zijn om de oorspronkelijke taken (toestemmingscontrole, bedrijfslogica, API‑verwerking) uit te voeren. In dat geval is het een gangbare techniek om toestemmingscontrole door Django te laten doen en de daadwerkelijke bestandslevering door Nginx te laten uitvoeren via X-Accel-Redirect.
Waarom leidt het rechtstreeks verzenden van bestanden door Python tot een bottleneck?
De typische workflow van Django die bestanden rechtstreeks levert ziet er als volgt uit:
- Verzoek ontvangen
- Toestemmingscontrole
- Bestand lezen van schijf/opslag
- De applicatie‑processen sturen het bestand via het netwerk (streaming)
Het probleem is dat stappen 3 en 4 “zware taken” zijn.
- Hoe groter het bestand, hoe langer de overdracht duurt
- Bij meer gelijktijdige downloads worden workers/threads/processen steeds meer geblokkeerd
- Dit leidt tot vertraagde API‑responsen, time‑outs en de noodzaak om de server te schalen
Nginx daarentegen is geoptimaliseerd voor het leveren van statische bestanden, met kernel‑niveau optimalisaties (sendfile), een efficiënte event‑loop, buffering en range‑verzoeken.
De kernidee van X-Accel-Redirect
Django doet alleen de controle, Nginx doet alleen de overdracht.
Werking
- De cliënt vraagt een URL zoals
/download/123 - Django voert alleen een database‑query en toestemmingscontrole uit
- Django stuurt een lege body terug met de volgende header:
X-Accel-Redirect: /_protected/real/path/to/file.webp4. Nginx ziet deze header, zoekt het bestand intern en stuurt het rechtstreeks naar de cliënt
Django leest dus nooit het bestand zelf. Het is dus verantwoordelijk voor “mag dit bestand wel worden gedownload?” en Nginx voor “lever het bestand”.
Wanneer is deze aanpak bijzonder nuttig?
De volgende situaties profiteren het meest.
1) Veel download‑/afbeeldingsverzoeken met hoge gelijktijdigheid
- Community‑/messenger‑afbeeldingen, bijlagen, PDF‑rapporten
- Patronen met veel verzoeken en eenvoudige logica
2) Grote bestanden of belangrijke range‑verzoeken
- Video, audio, grote zip‑bestanden
- Browsers/spelers die
Range(segmentverzoeken) gebruiken → Nginx verwerkt dit veel stabieler
3) Kosten van de app‑server verlagen
- Python‑workers zijn duur (geheugen/CPU) en “verstrikt” raken bij bestandslevering
- Door de bestandslevering naar een proxy‑laag te verplaatsen, kan de app‑server zich concentreren op logica
Wanneer kun je het ook overslaan?
- Interne server‑naar‑server communicatie met laag verkeer
- Weinig bestandsverzoeken en de bottleneck ligt bij API/DB‑logica
- Bestanden zijn op een externe object‑opslag (S3, etc.) en worden al via CDN/vooraf‑ondertekende URL’s bediend
In deze gevallen is FileResponse nog steeds voldoende. Het is nooit te laat om later over te schakelen.
Implementatievoorbeeld: Django + Nginx

Nginx‑configuratievoorbeeld
Het belangrijkste is het gebruik van internal.
Een locatie met internal is niet direct toegankelijk voor de cliënt en kan alleen via een interne redirect zoals X‑Accel‑Redirect.
# De interne endpoint die de beschermde bestanden daadwerkelijk serveert
location /_protected/ {
internal;
# De directory waar de echte bestanden staan
alias /var/app/protected_media/;
# Prestatieopties (pas aan op je omgeving)
sendfile on;
tcp_nopush on;
# Optioneel: cache/headers beheren
# add_header Cache-Control "private, max-age=0";
}
- Er wordt uitgegaan van bestanden onder
/var/app/protected_media/ - De publieke URL is een Django‑route zoals
/download/... - De interne route is uniform
/_protected/...
Django‑viewvoorbeeld
Django controleert alleen de toestemming en geeft geen bestands‑I/O.
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) DB‑query + toestemmingscontrole
obj = get_file_object_or_404(file_id) # voorbeeld
if not obj.can_download(request.user):
raise Http404
# 2) Interne pad samenstellen (onder /_protected/ van Nginx)
internal_path = f"/_protected/{obj.storage_relpath}"
# 3) Alleen X-Accel-Redirect header instellen, body leeg
response = HttpResponse()
response["X-Accel-Redirect"] = iri_to_uri(internal_path)
# (Optioneel) Bestandnaam/Content‑Type
response["Content-Type"] = obj.content_type or "application/octet-stream"
response["Content-Disposition"] = f'attachment; filename="{obj.download_name}"'
return response
Belangrijk:
- Geen
FileResponse(open(...))of andere I/O - De verwerkingstijd per verzoek is zeer kort en workers blokkeren niet
Beveiligingschecklist
1) Interne paden moeten altijd door de server worden bepaald
- Zorg dat er geen
/_protected/../../etc/passwdvia client‑input kan ontstaan - Gebruik alleen “veilige relatieve paden” opgeslagen in de database of een whitelist‑methode
2) De Nginx‑location moet internal zijn
- Zonder
internalkan een gebruiker/_protected/...direct aanroepen en de beveiliging omzeilen
3) Toestemmingslogica vertrouwt alleen op Django
- Nginx is alleen een leveringsmotor; alle toegangscontrole gebeurt in Django
Alternatieven met externe diensten
Als kosten geen probleem zijn, kun je overwegen om bestanden rechtstreeks via een externe opslagdienst te leveren. Dit bespaart server‑resources en kan betrouwbaarder zijn.
- CDN‑caching: voor publieke bestanden is een CDN vóór Nginx vaak effectiever
- Vooraf‑ondertekende URL’s (S3, etc.): voor object‑opslag is een pre‑signed URL vaak eenvoudiger dan X‑Accel‑Redirect
Conclusie
Kortom, het serveren van bestanden via Nginx in plaats van via de webapplicatie levert een duidelijke prestatie‑voordeel op. Nginx, geoptimaliseerd voor statische overdracht, maakt kernel‑optimalisaties en een efficiënte event‑loop optimaal benut. Hierdoor kan de app‑server zich richten op toestemmingscontrole en bedrijfslogica, zelfs bij hoge download‑traffic.
Voor lage traffic is FileResponse nog steeds een nette en voldoende keuze. Maar wanneer het aantal bestandsverzoeken snel toeneemt, is X‑Accel‑Redirect de snelste manier om de app‑server te beschermen.
Een simpel geheugensteuntje: “Toestemming in Django, overdracht in Nginx.”