Serve static files direct from WSGI application
WSGI middleware for easy serving of static files, with optional integration with Django. Secure and efficient enough to use in production.
Just wrap your WSGI application in the WhiteNoise middleware, give it the path to your static files directory and any requests matching files in that directory will be served correctly. All other requests are passed on to your application.
- Simple to configure
- Handles caching (sends cache headers and returns Not Modified responses when appropriate)
- Serves gzipped content (handling Accept-Encoding and Vary headers correctly)
- Provides hooks for easy customisation, e.g. sending custom headers for certain files
- Can serve static files from arbitrary URLs, not just from a fixed URL prefix, so you can use it to serve files like /favicon.ico or /robots.txt
- Python 2/3 compatibile
Shouldn’t I be using a real webserver, or a CDN, or Amazon S3? See Infrequently Asked Questions
QuickStart: Standard WSGI application
from whitenoise import WhiteNoise from my_project import MyWSGIApp application = MyWSGIApp() application = WhiteNoise(application, root='/path/to/static/files') application.add_files('/path/to/more/static/files', prefix='more-files/')
QuickStart: Django application
from django.core.wsgi import get_wsgi_application from whitenoise.django import DjangoWhiteNoise application = get_wsgi_application() application = DjangoWhiteNoise(application)
This will automatically serve the files in STATIC_ROOT under the prefix derived from STATIC_URL.
If it detects that you are using CachedStaticFilesStorage it will automatically set far-future Expires headers on your static content.
QuickStart: Pre-gzipping content
WhiteNoise comes with a command line utility which will create gzip-compressed versions of files in a directory. WhiteNoise will then serve these compressed files instead, where the client indicates that it accepts them.
$ python -m whitenoise.gzip --help usage: gzip.py [-h] [-q] [-f] root [extensions [extensions ...]] Search for all files inside <root> matching <extensions> and produce gzipped versions with a '.gz' suffix (as long this results in a smaller file. positional arguments: root Path root from which to search for files extensions File extensions to match (default: (u'css', u'js')) optional arguments: -h, --help show this help message and exit -q, --quiet Don't produce log output (default: False) -f, --force Overwrite pre-existing .gz files (default: False)
There is also a Django management command which wraps this utility to gzip the contents of your STATIC_ROOT directory. (Note that you’ll need to add whitenoise to your list of INSTALLED_APPS if you want to use this command.)
$ python manage.py gzipstatic --help Usage: ./manage.py gzipstatic [options] Search for all files in STATIC_ROOT matching the extensions specified in WHITENOISE_GZIP_EXTENSIONS (by default: css, js) and produce gzipped versions with a '.gz' suffix Options: -v VERBOSITY, --verbosity=VERBOSITY Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, 3=very verbose output --settings=SETTINGS The Python path to a settings module, e.g. "myproject.settings.main". If this isn't provided, the DJANGO_SETTINGS_MODULE environment variable will be used. --pythonpath=PYTHONPATH A directory to add to the Python path, e.g. "/home/djangoprojects/myproject". --traceback Print traceback on exception --quiet Don't produce any log ouput --force Overwrite pre-existing .gz files --version show program's version number and exit -h, --help show this help message and exit
Infrequently Asked Questions
Shouldn’t I be using a real webserver?
Well, perhaps. Certainly something like nginx will be more efficient at serving static files. But here are a few things to consider:
- There are situations (e.g., when hosted on Heroku) where it’s much simpler to have everything handled by your Python application.
- WhiteNoise is pretty efficient. Because it only has to serve a fixed set of files it does as much work as it can upfront on initialization, meaning it can serve responses with very little work. Also, when used with gunicorn (and most other WSGI servers) the actual business of pushing the file down the network interface is handled by the kernel’s highly efficient sendfile syscall, not by Python.
- If you’re using WhiteNoise behind a CDN or caching proxy (on which more below) then it doesn’t really matter that it’s not as efficient as nginx as the vast majority of static requests will be cached by the CDN and never touch your application.
Shouldn’t I be using a CDN?
Yes, given how cheap and straightforward they are these days, you probably should. But you should be using WhiteNoise to act as the origin, or upstream, server to your CDN.
Under this model, the CDN acts as a caching proxy which sits between your application and the browser (only for static files, you still use your normal domain for dynamic requests). WhiteNoise will send the appropriate cache headers so the CDN can serve requests for static files without hitting your application.
Shouldn’t I be pushing my static files to S3 using something like Django-Storages?
The correct behaviour is to examine the Accept-Encoding header of the request to see if gzip is supported, and to return an appropriate Vary header so that intermediate caches know to do the same thing. This is exactly what WhiteNoise does.
Release history Release notifications | RSS feed
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
|Filename, size||File type||Python version||Upload date||Hashes|
|Filename, size whitenoise-0.10-py2.py3-none-any.whl (12.5 kB)||File type Wheel||Python version 2.7||Upload date||Hashes View|
|Filename, size whitenoise-0.10.tar.gz (86.5 kB)||File type Source||Python version None||Upload date||Hashes View|
Hashes for whitenoise-0.10-py2.py3-none-any.whl