10đź‘Ť
Your comment:
The whitenoise documentation here says that we should not use both the middlewares and I just wanted to know if there was a drawback of using whitenoise over django.security – Manan Mehta 30 mins ago
No, it doesn’t say that – that section of the documentation refers to the order of MIDDLEWARE_CLASSES. You can happily use Whitenoise with Django’s security middleware.
Excerpt from the docs below:
Edit your settings.py file and add WhiteNoise to the MIDDLEWARE_CLASSES list, above all other middleware apart from Django’s SecurityMiddleware:
MIDDLEWARE_CLASSES = [
# 'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
# ...
]
SnakeFcz’s recommendation to force Django to serve static is not a good idea – the Whitenoise package is designed as a high performance method to serve static through Django. See the heroku docs and the Whitenoise docs.
Reply to your edit:
In Django, static refers to things like JS/CSS/images that you create during development that don’t change (remains static) when your app is deployed. Media are things like user uploaded images and video, or generated images (e.g. thumbnails).
Django recommends storing static and media separately, in STATIC_ROOT and MEDIA_ROOT directories. Then in production you typically configure a web server at these directories for the URLs STATIC_URL and MEDIA_URL. Whitenoise simplifies things a bit by serving files from these folders correctly, without having to configure a web server.
The main concerns for getting Django to serve images correctly (static or media) are as follows:
-
Performance – Django is not optimised for serving assets, so it’ll be slower than serving through a web server such as Nginx/Apache. Lots of image requests will also slow down standard page requests, because they’ll queue up and cause a longer response time. This might not matter when your website is small, but changing the way your website works when you have traffic is tricky!
-
Caching headers – Django doesn’t know to add cache-control headers to images when they’re returned, whereas packages like Whitenoise add sensible caching headers (the most important being the cache expiry, e.g. how long for user’s browsers to hang onto your images).
There are some other headers that Whitenoise handles that Django might not, depending on the way you return an image:
-
Media types – browsers need to know how to deal with the response they’re getting, so there’s a header called
Content-Type
. With your code above, you’re returning every file as an image – what if the user asks for a PNG? -
Content length – browsers use the content length (size of the response) to show progress bars, and other optimisation (such as reading the response in chunks).
-
Compression – most browsers and web servers (and Whitenoise) support compression methods such as gzip or more recently brotli (built by google). The web server compresses the file (usually once, and then the compressed file is cached) to minimise bandwidth during transfer. Depending on the image and format, you can often compress images down to around 60-70% of their size.
Demo on lena bitmap:
❯ brew install gzip brotli ❯ gzip -k -v lena.bmp lena.bmp: 18.3% -- replaced with lena.bmp.gz ❯ bro --input lena.bmp --output lena.bmp.bro ❯ ls -lh lena* -rw-r--r--@ 1 alex staff 768K Feb 16 21:41 lena.bmp -rw------- 1 alex staff 527K Feb 16 21:45 lena.bmp.bro -rw-r--r--@ 1 alex staff 627K Feb 16 21:41 lena.bmp.gz
-
Security – another reason to leave serving static assets to the web server is the potential for security exploits!
Let’s assume the code in your view to serve images is as follows, and the url is set up at
static/<filename>
.img = os.path.join(settings.MEDIA_ROOT, filename) with open(img, "rb") as f: return HttpResponse(f.read(), content_type="image/jpeg")
Imagine if a malicious user navigated to
yoursite.com/static//Users/alex/.ssh/id_rsa
. Then filename becomes/Users/alex/.ssh/id_rsa
:filename = '/Users/alex/.ssh/id_rsa' os.path.join(settings.MEDIA_ROOT, filename) # '/Users/alex/.ssh/id_rsa'
Then the view reads in your web server’s private key, and return it to the malicious user. Whoops! Now they can ssh into your server.
Media on Heroku:
One thing to bear in mind if you’re deploying to Heroku is the way their dynos work. Heroku dynos are created and destroyed quite often (every time you deploy, and at least every day) so you can’t rely on the file system to stay around. You can also run two or more dynos at once – these are completely separate containers run on different hosts in a data centre, they don’t share a filesystem. Usually if you want to handle user uploaded media, you’ll use Django-storages to store images on S3 (AWS’s storage service) instead of the filesystem. You could also store images in your database, but that doesn’t scale as nicely. See https://github.com/eknuth/django-heroku-s3-bootstrap-demo for an example Django app set up to store media on S3.