skip to content

What does WhiteNoise's CompressedManifestStaticFilesStorage add on top of Django's ManifestStaticFilesStorage, and how are the compressed files served?

level: middleimportance: nice to knowfreq 30%

answer

  1. compress once, at build time
  2. two encodings beside each file
  3. only when it actually shrinks
  4. negotiated by Accept-Encoding

basics

~10 s

CompressedManifestStaticFilesStorage hashes files like Django's ManifestStaticFilesStorage, then writes .gz and, with the Brotli package, .br copies during collectstatic; WhiteNoiseMiddleware serves the smallest variant the browser's Accept-Encoding allows.

solid answer

~40 s

`whitenoise.storage.CompressedManifestStaticFilesStorage` subclasses Django's `ManifestStaticFilesStorage`, so it produces the same hashed names and `staticfiles.json`. After that post-processing it compresses the files: a `.gz` copy at gzip level 9, and a `.br` copy if the Brotli package is installed (`whitenoise[brotli]`). A variant is kept only if it is at most 95% of the original size, and extensions listed in `WHITENOISE_SKIP_COMPRESS_EXTENSIONS` (images, fonts, archives) are skipped. At request time `WhiteNoiseMiddleware` picks the smallest variant the `Accept-Encoding` header allows and adds `Vary: Accept-Encoding`. It also reads `WHITENOISE_MANIFEST_STRICT` and `WHITENOISE_KEEP_ONLY_HASHED_FILES`, and rewrites Django's missing-file `ValueError` into a clearer message naming the CSS or JS file at fault.

code

python · 9 lines
python
STORAGES = {
    'default': {'BACKEND': 'django.core.files.storage.FileSystemStorage'},
    'staticfiles': {
        'BACKEND': 'whitenoise.storage.CompressedManifestStaticFilesStorage',
    },
}

# optional: drop unhashed originals to halve STATIC_ROOT
WHITENOISE_KEEP_ONLY_HASHED_FILES = True

go deeper

for a junior

Recall that the WhiteNoise backend writes gzip and Brotli copies during collectstatic and that Brotli needs the whitenoise[brotli] extra.

for a middle

Explain the 95% rule, the skip list, and how the middleware picks the smallest variant from Accept-Encoding and sets Vary.

for a senior

Decide on WHITENOISE_KEEP_ONLY_HASHED_FILES and build size, and read MissingFileError output to fix broken references quickly.

for a principal

Weigh build-time compression in the app image against leaving compression to a CDN, keeping one owner for encodings and cache keys.

## What it is `whitenoise.storage.CompressedManifestStaticFilesStorage` is a storage backend you select in `STORAGES['staticfiles']['BACKEND']`. It is a subclass of Django's `ManifestStaticFilesStorage`, so everything that class does still happens during `collectstatic`: content-hashed copies, rewritten CSS references and a `staticfiles.json` manifest. WhiteNoise adds work **after** that post-processing. ## What collectstatic produces 1. Django's manifest post-processing runs and yields each original and hashed file. 2. WhiteNoise records which names are the final hashed ones. 3. If `WHITENOISE_KEEP_ONLY_HASHED_FILES` is `True`, the unhashed originals and intermediate files are deleted; by default (`False`) both originals and hashed copies are kept. 4. Each remaining file whose extension is not in `WHITENOISE_SKIP_COMPRESS_EXTENSIONS` is compressed in a thread pool: - **Brotli** first, if the `brotli` package is importable; the `.br` file is kept only if it is **at most 95%** of the original size. If Brotli did not help, gzip is skipped too. - **gzip** at level 9 with a fixed zero timestamp, so identical input always produces identical `.gz` output; kept under the same 95% rule. With the defaults, one source file can end up as six files: original, hashed, and a `.gz` and `.br` of each. ## Why compress at build time | Approach | When compression happens | Cost per request | |---|---|---| | WhiteNoise storage backend | once, during `collectstatic` | none: bytes are read from disk | | Django's `GZipMiddleware` | on every response | CPU for every asset response | | No compression | never | larger transfers | Pre-compression also allows the maximum compression level, which would be too slow to apply on each request. ## How the compressed files are served When WhiteNoise indexes `STATIC_ROOT` at start-up, it notices `app.55e7cbb9ba48.css.br` and `.gz` next to `app.55e7cbb9ba48.css` and records them as alternatives. For each request: - The alternatives are sorted by size, smallest first. - The first one the request's `Accept-Encoding` header allows is served, with a matching `Content-Encoding`. - If none matches, the uncompressed file is served. - Whenever alternatives exist, the response carries `Vary: Accept-Encoding`, so shared caches keep one copy per encoding. Browsers only request Brotli over HTTPS, so plain-HTTP clients fall back to gzip. ## Settings it reads - `WHITENOISE_SKIP_COMPRESS_EXTENSIONS`: defaults to already-compressed types such as `jpg`, `png`, `webp`, `woff2`, `zip` and `gz`. - `WHITENOISE_KEEP_ONLY_HASHED_FILES`: default `False`; `True` shrinks the build by dropping unhashed originals, which templates should never reference anyway. - `WHITENOISE_MANIFEST_STRICT`: when set, overrides the storage's `manifest_strict` attribute; even with `False`, a file that does not exist still raises an error. ## Friendlier failures Django's post-processing raises `ValueError` when a CSS or JS file references a file that cannot be found. WhiteNoise intercepts that error and re-raises it as `MissingFileError`, a `ValueError` subclass whose message names the file containing the bad reference and the missing target, so the failure is not mistaken for a WhiteNoise bug.

  • Why doesn't WhiteNoise compress PNG or WOFF2 files by default?
    Those formats are already compressed, so gzip or Brotli would rarely save anything and would only slow `collectstatic`. They are in the default `WHITENOISE_SKIP_COMPRESS_EXTENSIONS`. Even for other types, WhiteNoise keeps a compressed copy only when it is at most 95% of the original, so an unhelpful variant is never served.
  • Is it safe to set WHITENOISE_KEEP_ONLY_HASHED_FILES = True?
    Yes, if every reference goes through `{% static %}` or the storage's `url()`, which emit hashed names. Any hand-written `/static/app.css` path or a third-party tool expecting the unhashed name would then 404, so check for those before turning it on.

saying these in an interview costs you the question

  • WhiteNoise compresses every response on the fly like GZipMiddleware.
  • Brotli files appear without installing anything extra.
  • A compressed copy is written for every file, even if it is bigger.
  • CompressedManifestStaticFilesStorage replaces Django's hashing with its own scheme.
  • WhiteNoise always serves the .br file because every browser supports Brotli.