WhiteNoise

WhiteNoise ist ein Python-Paket, mit dem statische Dateien direkt aus einer WSGI-Anwendung ausgeliefert werden können. Für kleinere und mittlere Deployments ist das ein pragmatischer Weg, um CSS-, JavaScript- und Bilddateien ohne zusätzlichen dedizierten Static-File-Server bereitzustellen. WhiteNoise unterstützt komprimierte Auslieferung und setzt für unveränderliche Dateien Cache-Header, sodass Browser diese Dateien lange zwischenspeichern können.

Für Django ist WhiteNoise besonders praktisch, weil es sich eng in das staticfiles-System integriert. Django sammelt statische Dateien aus Apps und weiteren konfigurierten Verzeichnissen zentral ein, und WhiteNoise kann genau dieses gesammelte Verzeichnis effizient ausliefern. :contentReference[oaicite:2]{index=2}

Warum brauchen wir das?

Im Entwicklungsbetrieb kann Django statische Dateien selbst bereitstellen. Im Produktivbetrieb ist das jedoch nicht die empfohlene Standardstrategie. Dort werden statische Dateien normalerweise zunächst gesammelt und dann von einer dafür geeigneten Komponente ausgeliefert. WhiteNoise übernimmt genau diese Aufgabe innerhalb der WSGI-Anwendung.

Der Grund liegt weniger bei Django selbst als beim eingesetzten WSGI-Server (z. B. Gunicorn). Dieser ist darauf ausgelegt, Python-Code auszuführen und Requests an die Anwendung weiterzuleiten, nicht jedoch darauf, statische Dateien effizient auszuliefern. Jeder Request belegt einen Worker-Prozess, wodurch statische Inhalte unnötig Ressourcen verbrauchen und die Skalierbarkeit einschränken.

Aus diesem Grund werden statische Dateien im Produktivbetrieb in der Regel von spezialisierten Komponenten ausgeliefert, etwa durch WhiteNoise, einen Reverse Proxy wie Nginx oder externe Dienste wie CDNs (AWS, Azure etc.).

Der große Vorteil ist, dass wir keine zusätzliche Infrastruktur nur für statische Dateien benötigen. Gleichzeitig profitieren wir von Komprimierung und Browser-Caching, was Ladezeiten und übertragene Datenmenge reduziert.

WhiteNoise installieren

Wir fügen WhiteNoise mit uv als Abhängigkeit hinzu:

uv add whitenoise

WhiteNoise unterstützt aktuell Django 4.2 bis 6.0 und wird offiziell für die Integration mit Django dokumentiert.

Grundlagen: STATIC_URL, STATIC_ROOT und collectstatic

Damit Django mit statischen Dateien arbeiten kann, brauchen wir zunächst eine STATIC_URL. Diese URL wird in Templates über das static-Template-Tag verwendet. Wenn django.contrib.staticfiles installiert ist, erzeugt dieses Tag die URL mithilfe des staticfiles-Backends aus STORAGES.

Zusätzlich benötigen wir ein Zielverzeichnis, in das alle statischen Dateien gesammelt werden. Dieses Verzeichnis wird über STATIC_ROOT definiert.

Der Befehl collectstatic sammelt alle statischen Dateien aus den einzelnen Apps und definierten Verzeichnissen und kopiert sie in ein zentrales Zielverzeichnis (STATIC_ROOT). Der Grund dafür ist, dass statische Dateien im Produktivbetrieb gebündelt an einem Ort vorliegen müssen, damit sie effizient von einer Komponente wie WhiteNoise, Nginx oder einem CDN ausgeliefert werden können.

../_images/collect_static_mermaid.png

Eine minimale Basiskonfiguration sieht so aus:

STATIC_URL = "static/"
STATIC_ROOT = BASE_DIR / "staticfiles"

Bemerkung

collectstatic im Kopf behalten

Änderungen an CSS-, JavaScript- oder Bilddateien werden im Produktivbetrieb nicht automatisch sichtbar. Nach Änderungen an statischen Dateien muss collectstatic erneut ausgeführt werden, damit das gesammelte Verzeichnis aktualisiert wird. :

WhiteNoise in die Middleware einbinden

WhiteNoise wird in Django als Middleware eingebunden, und zwar direkt hinter der SecurityMiddleware:

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "whitenoise.middleware.WhiteNoiseMiddleware",
    # ...
]

Diese Reihenfolge wird von WhiteNoise ausdrücklich empfohlen.

Lokale Entwicklung mit runserver_nostatic

Django kann im Entwicklungsmodus statische Dateien selbst ausliefern. Wenn wir aber lokal möglichst nah am späteren Produktivverhalten arbeiten möchten, ist es sinnvoll, diese eingebaute Entwicklungshilfe zu deaktivieren und WhiteNoise auch lokal zu verwenden. Dafür stellt WhiteNoise die App whitenoise.runserver_nostatic bereit.

Sie wird vor django.contrib.staticfiles in INSTALLED_APPS eingetragen:

INSTALLED_APPS = [
    "whitenoise.runserver_nostatic", # <<< Hier eintragen!
    "django.contrib.staticfiles",
    # ...
]

Dadurch übernimmt WhiteNoise die Auslieferung statischer Dateien auch beim lokalen runserver.

Konfiguration ab Django 4.2 mit STORAGES

Seit Django 4.2 werden File-Storage-Backends über die Einstellung STORAGES konfiguriert. Die früheren Einstellungen DEFAULT_FILE_STORAGE und STATICFILES_STORAGE wurden dabei als veraltet markiert. Für statische Dateien ist der Schlüssel "staticfiles" zuständig, für normale hochgeladene Dateien der Schlüssel "default".

Für WhiteNoise konfigurieren wir den staticfiles-Storage so:

STORAGES = {
    "staticfiles": {
        "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage",
    },
    "default": {
        "BACKEND": "django.core.files.storage.FileSystemStorage",
    },
}

Diese Konfiguration bedeutet:

default

Das Standard-Storage-Backend für normale Dateien, also typischerweise Uploads.

staticfiles

Das Storage-Backend, das vom staticfiles-System für statische Dateien verwendet wird, also insbesondere beim collectstatic-Prozess und beim Erzeugen von URLs über {% static %}.

Was bedeutet CompressedManifestStaticFilesStorage?

Der Name ist lang, beschreibt aber ziemlich genau, was dieses Backend tut:

Compressed

WhiteNoise erzeugt zusätzlich komprimierte Varianten statischer Dateien. Laut Dokumentation unterstützt WhiteNoise komprimierte Auslieferung, unter anderem mit gzip und Brotli, und berücksichtigt dabei passende Request-Header wie Accept-Encoding.

Manifest

Die Dateinamen statischer Dateien werden mit einem inhaltsabhängigen Hash versehen. Aus app.css wird dann beispielsweise etwas wie app.4f3c1a2b.css. Der Vorteil ist Cache-Busting: Ändert sich der Inhalt, ändert sich auch der Dateiname. Browser dürfen solche Dateien daher sehr lange cachen, ohne dass Nutzer veraltete Dateien sehen. Django beschreibt dieses Verhalten über ManifestStaticFilesStorage; WhiteNoise baut darauf auf.

Das Manifest selbst ist eine Zuordnung zwischen dem ursprünglichen statischen Pfad und der gehashten Zieldatei. Wenn im Template oder in verarbeiteten CSS-Dateien auf eine statische Datei verwiesen wird, kann Django beziehungsweise das Storage-Backend so die korrekte gehashte Datei-URL ermitteln. Dadurch funktionieren langfristiges Caching und konsistente Referenzen zusammen. Diese Auflösung der URL erfolgt über das konfigurierte staticfiles-Storage.

Warnung

Keine manuellen Pfade zu Static Files

Statische Dateien sollten im Template nicht hart verdrahtet werden, etwa über /static/css/site.css. Stattdessen sollte immer {% load static %} und anschließend {% static 'css/site.css' %} verwendet werden. Nur so kann Django die vom Storage erzeugten gehashten Dateinamen korrekt auflösen.

Vollständige Beispielkonfiguration

Eine typische Konfiguration könnte so aussehen:

INSTALLED_APPS = [
    "whitenoise.runserver_nostatic",
    "django.contrib.staticfiles",
    # weitere Apps ...
]

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "whitenoise.middleware.WhiteNoiseMiddleware",
    # weitere Middleware ...
]

STATIC_URL = "static/"
STATIC_ROOT = BASE_DIR / "staticfiles"

STORAGES = {
    "staticfiles": {
        "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage",
    },
    "default": {
        "BACKEND": "django.core.files.storage.FileSystemStorage",
    },
}

So ist sichergestellt, dass statische Dateien gesammelt, mit Hash versehen, komprimiert und anschließend von WhiteNoise ausgeliefert werden.

collectstatic im Detail

Der Befehl collectstatic durchsucht alle statischen Quellen, also insbesondere die static-Verzeichnisse deiner Apps und weitere konfigurierte Orte, und kopiert deren Inhalte in STATIC_ROOT. Zusätzlich darf das Storage-Backend während dieses Prozesses Dateien nachbearbeiten. Django erwähnt explizit die post_process-Phase des Storage-Backends. Genau hier greifen Backends wie das WhiteNoise-Storage ein und erzeugen beispielsweise gehashte und komprimierte Varianten.

Der Ablauf ist also vereinfacht:

  1. Django findet statische Dateien in Apps und konfigurierten Verzeichnissen.

  2. Diese Dateien werden nach STATIC_ROOT gesammelt.

  3. Das konfigurierte staticfiles-Storage verarbeitet sie nach.

  4. WhiteNoise kann daraus komprimierte und gehashte Fassungen erzeugen.

Ausgeführt wird der Befehl so:

uv run manage.py collectstatic

Nach dem Lauf befinden sich die gesammelten Dateien in STATIC_ROOT. Dort können dann neben der Originaldatei auch gehashte oder komprimierte Varianten liegen, abhängig vom verwendeten Storage-Backend. WhiteNoise beschreibt genau dieses Verhalten für sein komprimierendes Manifest-Storage.

Wie wirkt sich das im Template aus?

Im Template ändert sich für uns fast nichts. Wir verwenden weiterhin das static-Tag:

{% load static %}
<link rel="stylesheet" href="{% static 'css/site.css' %}">
<img src="{% static 'images/logo.png' %}" alt="Logo">

Der wichtige Punkt ist: Das Template verwendet den logischen Pfad, nicht den gehashten Dateinamen. Die eigentliche Auflösung übernimmt das konfigurierte staticfiles-Storage. Dadurch bleibt der Template-Code lesbar, obwohl im Hintergrund eine gehashte Datei-URL erzeugt wird.

Was bringt das Caching konkret?

Bei gehashten statischen Dateien darf WhiteNoise sehr aggressive Cache-Header setzen, weil sich der Dateiname bei jeder Inhaltsänderung mitändert. Ein Browser kann eine solche Datei also lange behalten. Wenn du später eine neue CSS-Datei deployest, bekommt sie einen neuen Hash im Dateinamen und wird dadurch automatisch als neue Ressource erkannt. Genau dafür ist die Manifest-Strategie gedacht.

Das verbessert die Performance, weil Browser wiederholte Downloads vermeiden. Gleichzeitig verhindert der Hash im Dateinamen, dass Nutzer versehentlich alte CSS- oder JavaScript-Dateien weiterverwenden.

Typischer Ablauf im Deployment

Ein typischer Deployment-Ablauf sieht so aus:

  1. Abhängigkeiten installieren

  2. Django mit den Produktions-Settings starten

  3. uv run manage.py collectstatic ausführen

  4. Die Anwendung mit WhiteNoise laufen lassen

WhiteNoise liefert anschließend die zuvor gesammelten Dateien aus STATIC_ROOT aus. Django beschreibt den Produktionsablauf für statische Dateien ebenfalls genau in diesen Schritten: sammeln und anschließend ausliefern lassen.

Good practice: collectstatic als Deployment-Schritt

collectstatic sollte als fester Schritt im Deployment betrachtet werden. Wer neue statische Dateien deployt, ohne collectstatic auszuführen, riskiert fehlende oder veraltete Ressourcen im Produktivsystem. In der Regel wird dieser Schritt heutzutage automatisiert, etwa über CI/CD-Pipelines oder Deployment-Skripte in Docker-Umgebungen, um menschliche Fehler zu vermeiden.

Grenzen von WhiteNoise

WhiteNoise ist eine sehr praktische Lösung, aber nicht in jedem Szenario die endgültige Antwort. Bei sehr großen Setups oder beim Einsatz eines CDN können andere Architekturen sinnvoll sein. Für viele klassische Django-Projekte ist WhiteNoise jedoch eine einfache und robuste Lösung, insbesondere wenn man Infrastruktur-Komplexität gering halten möchte. Diese Einordnung ergibt sich auch aus der WhiteNoise-Dokumentation, die WhiteNoise ausdrücklich als pragmatische Alternative zu externer Static-File-Infrastruktur positioniert.

Zusammenfassung

Mit WhiteNoise können statische Dateien direkt aus einer Django-WSGI-Anwendung ausgeliefert werden. Seit Django 4.2 wird das dafür relevante Backend über STORAGES["staticfiles"] konfiguriert. Das empfohlene Backend whitenoise.storage.CompressedManifestStaticFilesStorage sorgt dafür, dass statische Dateien komprimiert, mit Hash versehen und für langfristiges Browser-Caching geeignet bereitgestellt werden. Voraussetzung dafür ist, dass collectstatic beim Deployment zuverlässig ausgeführt wird.