When a Django app serves static files through WhiteNoise, when should you put a CDN in front, and what must you configure so it works?
answer
- every asset holds a worker
- STATIC_URL can be absolute
- the origin keeps serving /static/
- hashed names never go stale
- fonts across origins need CORS
basics
~20 sAdd a CDN once asset traffic or distant users make serving from the Django process costly: point STATIC_URL at the CDN host, let the CDN pull from WhiteNoise at the origin, and rely on WhiteNoise's immutable headers for hashed files.
solid answer
~50 sWhiteNoise alone is fine for moderate traffic, but every asset request occupies an application worker, and distant users pay the round trip. A CDN in front caches the files near users. Configure it by building `STATIC_URL` from a CDN host read from an environment variable, for example `STATIC_URL = os.environ.get('DJANGO_STATIC_HOST', '') + '/static/'`, so `{% static %}` emits CDN URLs; WhiteNoise keeps serving the path part, `/static/`, at the origin for the CDN to fetch. Hashed files carry `max-age=315360000, public, immutable`, so the CDN can keep them for years; unhashed files expire after 60 seconds. `Access-Control-Allow-Origin: *`, on by default, lets fonts load from the other origin. Make the CDN respect `Vary: Accept-Encoding`, restrict it to the static prefix, and bake assets into the image so every origin instance has them before new HTML is served.
code
python · 13 linesimport os
# settings.py
STATIC_HOST = os.environ.get('DJANGO_STATIC_HOST', '') # e.g. https://assets.example.com
STATIC_URL = STATIC_HOST + '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
STORAGES = {
'default': {'BACKEND': 'django.core.files.storage.FileSystemStorage'},
'staticfiles': {
'BACKEND': 'whitenoise.storage.CompressedManifestStaticFilesStorage',
},
}go deeper
Recall that STATIC_URL can point at a CDN host and that the CDN fetches from the Django app, where WhiteNoise still serves the files.
Explain why hashed files can be cached for years without purges and why unhashed ones get 60 seconds by default.
Anticipate cross-origin fonts, Accept-Encoding handling, whole-site exposure through the CDN and 404s during rolling deploys, and configure around each.
Decide when the added CDN, its cost and its failure modes are worth it compared with serving assets from the app processes.
## When WhiteNoise alone stops being enough WhiteNoise serves files efficiently, but it still serves them **from the Django process**. Each asset request occupies an application worker for as long as the transfer takes, and visitors far from the server pay the full network round trip for every file. WhiteNoise's own docs say it gives decent performance on moderate-traffic sites and recommend a CDN for higher traffic or where performance matters. The signals that it is time: - Asset requests make up a large share of worker time. - Users are spread far from the origin region. - Traffic spikes, such as a launch, would otherwise hit the app servers for static bytes. ## The shape of the set-up 1. The CDN is configured with the **Django app as its origin**. 2. `STATIC_URL` becomes an absolute URL on the CDN's host, so `{% static %}` renders links to the CDN. 3. The browser asks the CDN; on a miss, the CDN fetches the same path from the origin, where WhiteNoise serves it with its caching headers. 4. Later requests for that URL are answered by the CDN until its cached copy expires. WhiteNoise derives its URL prefix from the **path** component of `STATIC_URL`, so an absolute `STATIC_URL` still leaves the origin serving `/static/`. Only a CDN that rewrites paths needs `WHITENOISE_STATIC_PREFIX` set explicitly. ```python import os STATIC_HOST = os.environ.get('DJANGO_STATIC_HOST', '') STATIC_URL = STATIC_HOST + '/static/' ``` Reading the host from the environment keeps development on local URLs and lets each environment use its own CDN host without code changes. ## What makes it safe to cache for years | File | Header from WhiteNoise | What the CDN can do | |---|---|---| | Hashed, confirmed by the manifest | `max-age=315360000, public, immutable` | keep it effectively forever | | Unhashed | `max-age=60, public` (default `WHITENOISE_MAX_AGE`) | keep it for a minute | A hashed URL can never refer to different bytes, so a deploy never needs a purge for it: new content has a new name, new HTML points at it, and the CDN fetches it on first request. That only holds if templates use `{% static %}` with a manifest-backed storage. ## The details that break it - **Cross-origin fonts.** Assets now come from a different origin than the page. Fonts, and images read back from a canvas, need CORS. `WHITENOISE_ALLOW_ALL_ORIGINS` defaults to `True` and sends `Access-Control-Allow-Origin: *`; turning it off breaks them. - **Compression.** WhiteNoise serves `.br` or `.gz` based on `Accept-Encoding` and sends `Vary: Accept-Encoding`. The CDN must forward that header and cache per encoding, otherwise users may receive only one encoding, or the CDN may substitute its own compression for WhiteNoise's pre-built files. - **The whole site through the CDN.** With the app as origin, every path can be fetched through the CDN host; restrict the CDN to the static prefix to avoid duplicate pages being indexed. - **Rolling deploys.** New HTML can reach users before every origin instance has the new hashed files, and a request that lands on an old instance gets a 404. Bake `collectstatic` output into the image so each instance starts with its assets, and roll out instances before traffic shifts. - **Old hashed files.** A fresh image contains only the current build's hashed files. Pages cached before the deploy may still request the previous ones; the CDN usually still holds them, but if not, the origin returns 404. ## What stays out of this set-up User uploads are not served through WhiteNoise at all, so putting them behind a CDN is a storage-backend decision, not a WhiteNoise one.
- After a deploy behind a CDN, do you need to purge the cache for changed CSS?Not for files referenced through `{% static %}` with a manifest-backed storage: changed content gets a new hashed name, new HTML points at it and the CDN fetches it on first request. Only unhashed URLs can serve stale content, and WhiteNoise gives those a 60-second max-age by default, so they refresh quickly without a purge.
- Fonts fail to load from the CDN host while CSS and images work; what do you check first?Fonts are fetched in CORS mode, so the response needs `Access-Control-Allow-Origin`. WhiteNoise sends `*` by default through `WHITENOISE_ALLOW_ALL_ORIGINS`; check it was not turned off, and that the CDN passes the header through rather than stripping it from cached responses.
saying these in an interview costs you the question
- With a CDN in front, WhiteNoise can be removed from MIDDLEWARE.
- Every deploy requires purging the CDN or users see old CSS.
- Setting an absolute STATIC_URL stops WhiteNoise serving /static/ at the origin.
- WhiteNoise purges the CDN automatically when collectstatic runs.
- Access-Control-Allow-Origin can be disabled safely once a CDN serves fonts.