89/100 # Nginx übernimmt die Dateiübertragung: Mit X-Accel-Redirect die Download-Leistung in Django steigern Die meisten Django-Anwendungen liefern geschützte Dateien (z. B. nur für eingeloggte Nutzer oder nach dem Kauf verfügbar) über `FileResponse` oder ähnliche Mechanismen aus. Dabei liest der Python-Prozess die Datei und streamt sie an den Client. Bei geringem Traffic oder in internen Umgebungen ist das meist völlig ausreichend. Wenn die Zahl der Download-Anfragen jedoch stark ansteigt, wird der Anwendungsserver (Python) durch Datei-I/O gebunden: Worker hängen an Übertragungen, und es bleibt weniger Kapazität für Berechtigungsprüfungen, Geschäftslogik oder API-Antworten. Die Lösung: Django entscheidet, **wer** herunterladen darf – und Nginx übernimmt **die eigentliche Dateiübertragung** per **X-Accel-Redirect**. --- ## Warum direktes File-Streaming in Python zum Engpass wird {#sec-a393993c0d6e} Der typische Ablauf, wenn Django Dateien selbst ausliefert: 1. Anfrage empfangen 2. Berechtigungen prüfen 3. Datei von Datenträger/Storage lesen 4. Datei über den Applikationsprozess an den Client streamen Gerade **Schritt 3 und 4** sind teuer: * Große Dateien bedeuten lange Übertragungszeiten * Viele parallele Downloads binden Worker/Threads/Prozesse * Das führt zu langsamen API-Antworten, Timeouts und Skalierungsdruck Nginx ist dagegen für die Auslieferung statischer Dateien optimiert: Kernel-Optimierungen wie `sendfile`, effiziente Event-Loops, Buffering und Range-Request-Handling sind genau auf Dateiübertragungen ausgelegt. --- ## Kernidee von X-Accel-Redirect {#sec-957cf03b7472} **Django prüft, Nginx liefert aus.** ### So funktioniert es {#sec-b7ebc6f58a51} 1. Der Client ruft z. B. `/download/123` auf. 2. Django macht DB-Lookup und Berechtigungsprüfung. 3. Django antwortet mit leerem Body, aber mit Header: ``` X-Accel-Redirect: /_protected/real/path/to/file.webp ``` 4. Nginx erkennt den Header, findet die Datei intern und streamt sie direkt an den Client. Damit bestimmt Django nur *die Erlaubnis*, während Nginx *die Übertragung* übernimmt. --- ## Wann diese Methode besonders sinnvoll ist {#sec-262d95677742} ### 1) Viele Download-/Bildanfragen mit hoher Parallelität {#sec-c9ee78ddaabd} * Foren, Messenger, Anhänge, PDF-Downloads * Viele Requests bei vergleichsweise einfacher Logik ### 2) Große Dateien oder starke Nutzung von Range-Requests {#sec-30f4973c39f4} * Video, Audio, große Archive * Browser/Player fordern Byte-Ranges an; Nginx verarbeitet das zuverlässig ### 3) App-Server entlasten und Kosten senken {#sec-49e33c0e54e7} * Python-Worker sind teuer (RAM/CPU) * Offloading hält den App-Server frei für Business-Logik --- ## Wann man darauf verzichten kann {#sec-976147c06018} * Interne Kommunikation mit geringem Traffic * Wenige Datei-Requests, bei denen eher API/DB-Logik limitiert * Dateien liegen in S3/Objektspeichern und werden bereits via CDN oder Pre-Signed URLs ausgeliefert Dann ist `FileResponse` oft völlig ausreichend – und man kann X-Accel-Redirect später bei Bedarf nachziehen. --- ## Beispiel: Django + Nginx {#sec-8ead8af72381} ![Web-Request-Flowchart](/media/editor_temp/6/a1ad0374-979a-447e-b586-81ac02f2b447.png) ### Nginx-Konfiguration {#sec-5c330cb81155} Der zentrale Punkt ist `internal`: Ein `internal`-Location ist **nicht direkt** vom Client erreichbar, sondern nur über interne Redirects wie X-Accel-Redirect. ```nginx # Geschützte Dateien intern ausliefern location /_protected/ { internal; alias /var/app/protected_media/; sendfile on; tcp_nopush on; # Optional: Cache-/Header-Kontrolle # add_header Cache-Control "private, max-age=0"; } ``` * Reale Dateien liegen unter `/var/app/protected_media/` * Öffentliche URLs laufen über Django (z. B. `/download/...`) * Interne Pfade werden unter `/_protected/...` vereinheitlicht --- ### Django-View-Beispiel {#sec-c0ae54a01c3a} Django prüft nur die Berechtigung, ohne Datei-I/O. ```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) DB-Lookup + Berechtigungsprüfung obj = get_file_object_or_404(file_id) # Beispiel if not obj.can_download(request.user): raise Http404 # 2) Internen Pfad für Nginx bauen internal_path = f"/_protected/{obj.storage_relpath}" # 3) Nur X-Accel-Redirect setzen response = HttpResponse() response["X-Accel-Redirect"] = iri_to_uri(internal_path) # Optional: Dateiname und MIME-Type response["Content-Type"] = obj.content_type or "application/octet-stream" response["Content-Disposition"] = f'attachment; filename="{obj.download_name}"' return response ``` * Keine `FileResponse(open(...))`-I/O im View * Worker bleiben frei für andere Requests --- ## Sicherheits-Checkliste {#sec-075bc26a08d8} 1. **Interne Pfade serverseitig bestimmen** Nutzerinput nie direkt in `X-Accel-Redirect` übernehmen. Sichere relative Pfade aus der DB verwenden oder whitelisten. 2. **`internal` zwingend setzen** Sonst könnte `/_protected/...` direkt aufgerufen und der Check umgangen werden. 3. **Berechtigung ausschließlich in Django** Nginx liefert nur aus – Zugriffskontrolle muss in Django bleiben. --- ## Alternativen mit Drittanbietern {#sec-605774c09aa5} Wenn es passt, kann man Dateien auch direkt über externe Services ausliefern: * **CDN-Caching** für öffentliche Dateien * **Pre-Signed URLs (z. B. S3)** für Objektspeicher --- ## Fazit {#sec-e8fe764bfdda} X-Accel-Redirect verschiebt die teure Dateiübertragung vom App-Server zu Nginx. Django bleibt für Berechtigungen und Business-Logik zuständig, während Nginx die Dateien effizient streamt. Bei geringem Traffic ist `FileResponse` eine saubere Lösung – wenn aber die Download-Last steigt und Worker durch Datei-I/O blockieren, ist X-Accel-Redirect eine sehr schnelle und wirksame Optimierung. Merksatz: **„Berechtigungen in Django, Auslieferung in Nginx.“**