Radically simplified static file serving for WSGI applications
WhiteNoise provides seamless Django integration through middleware and static file storage classes. These components automatically configure themselves from Django settings and integrate with Django's static file handling system.
Django middleware that wraps the core WhiteNoise functionality, automatically configuring itself from Django settings and providing Django-specific optimizations.
class WhiteNoiseMiddleware(WhiteNoise):
"""
Django middleware wrapper for WhiteNoise.
Automatically configures from Django settings and provides Django-specific
features like staticfiles integration and development mode detection.
"""
def __init__(self, get_response=None, settings=settings):
"""
Initialize Django middleware.
Parameters:
- get_response: Django middleware get_response callable
- settings: Django settings module (defaults to django.conf.settings)
Auto-configures from Django settings:
- WHITENOISE_AUTOREFRESH (defaults to DEBUG)
- WHITENOISE_MAX_AGE (defaults to 0 if DEBUG else 60)
- WHITENOISE_ALLOW_ALL_ORIGINS (defaults to True)
- WHITENOISE_CHARSET (defaults to 'utf-8')
- WHITENOISE_MIMETYPES (defaults to None)
- WHITENOISE_ADD_HEADERS_FUNCTION (defaults to None)
- WHITENOISE_INDEX_FILE (defaults to None)
- WHITENOISE_IMMUTABLE_FILE_TEST (defaults to None)
- WHITENOISE_USE_FINDERS (defaults to DEBUG)
- WHITENOISE_STATIC_PREFIX (derived from STATIC_URL)
- WHITENOISE_ROOT (defaults to None)
"""
def __call__(self, request):
"""
Django middleware interface.
Parameters:
- request: Django HttpRequest object
Returns:
- Django HttpResponse object or None
"""
@staticmethod
def serve(static_file, request):
"""
Serve static file for Django request (static method).
Parameters:
- static_file: StaticFile instance
- request: Django HttpRequest object
Returns:
- WhiteNoiseFileResponse object
"""
def add_files_from_finders(self):
"""
Add files discovered by Django's staticfiles finders.
Integrates with Django's STATICFILES_FINDERS to serve files
from app directories and other configured locations.
"""
def get_name_without_hash(self, filename):
"""
Remove version hash from filename.
Parameters:
- filename: File name with potential hash
Returns:
- str: Filename without hash
Note: Specific to Django's CachedStaticFilesStorage naming scheme.
Override for custom versioning systems.
"""
def get_static_url(self, name):
"""
Get static URL using Django's static file storage.
Parameters:
- name: Static file name
Returns:
- str: Static URL or None if not found
"""Custom Django FileResponse that prevents default header setting to avoid conflicts with WhiteNoise's header management.
class WhiteNoiseFileResponse(FileResponse):
"""
Custom FileResponse that prevents default Django header setting.
Wraps Django's FileResponse to prevent setting default headers that
might conflict with WhiteNoise's comprehensive header management.
"""
def set_headers(self, *args, **kwargs):
"""Override to prevent automatic header setting."""Django static files storage backend that automatically compresses collected files.
class CompressedStaticFilesStorage(StaticFilesStorage):
"""
StaticFilesStorage subclass that compresses output files.
Automatically compresses files during Django's collectstatic process
using gzip and optionally Brotli compression.
"""
def post_process(
self,
paths: dict[str, Any],
dry_run: bool = False,
**options: Any
):
"""
Compress files after collection.
Parameters:
- paths: Dictionary of collected file paths
- dry_run: Skip actual compression if True
- options: Additional options
Yields:
- Iterator of (original_name, compressed_name, processed) tuples
Settings:
- WHITENOISE_SKIP_COMPRESS_EXTENSIONS: File extensions to skip
"""
def create_compressor(self, **kwargs):
"""
Create Compressor instance.
Parameters:
- kwargs: Arguments for Compressor constructor
Returns:
- Compressor: Compression utility instance
"""Advanced storage backend that combines Django's manifest static files with compression and optional file cleanup.
class CompressedManifestStaticFilesStorage(ManifestStaticFilesStorage):
"""
Extends ManifestStaticFilesStorage with compression and cleanup.
Combines Django's manifest-based static file versioning with
compression and optional removal of non-hashed files.
"""
def __init__(self, *args, **kwargs):
"""
Initialize with optional manifest strict mode.
Settings:
- WHITENOISE_MANIFEST_STRICT: Override manifest_strict behavior
"""
def post_process(self, *args, **kwargs):
"""
Process files with hashing and compression.
Extends parent post_process to add compression of hashed files
and optional cleanup of non-hashed originals.
Settings:
- WHITENOISE_KEEP_ONLY_HASHED_FILES: Remove non-hashed files
- WHITENOISE_SKIP_COMPRESS_EXTENSIONS: Extensions to skip
"""
def compress_files(self, paths):
"""
Compress collected and hashed files.
Parameters:
- paths: Iterable of file paths to compress
Yields:
- Iterator of (original_name, compressed_name) tuples
"""
def make_helpful_exception(self, exception, name):
"""
Create more helpful exception messages for missing file errors.
Parameters:
- exception: Original exception
- name: File name causing the error
Returns:
- Exception: Original or enhanced exception
"""Exception for static file collection errors with enhanced error messages.
class MissingFileError(ValueError):
"""
Exception raised when a referenced static file cannot be found.
Provides enhanced error messages to help debug missing file references
in CSS and other static files.
"""# settings.py
MIDDLEWARE = [
'whitenoise.middleware.WhiteNoiseMiddleware',
'django.middleware.security.SecurityMiddleware',
# ... other middleware
]
# Static files configuration
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
# Optional: Use compression
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'# settings.py
# WhiteNoise specific settings
WHITENOISE_USE_FINDERS = True # Enable in development
WHITENOISE_AUTOREFRESH = DEBUG # Auto-refresh in debug mode
WHITENOISE_MAX_AGE = 31536000 if not DEBUG else 0 # 1 year cache in production
# Skip compression for these extensions
WHITENOISE_SKIP_COMPRESS_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'zip', 'gz', 'tgz', 'bz2', 'tbz', 'xz', 'br']
# Keep only hashed files (removes originals)
WHITENOISE_KEEP_ONLY_HASHED_FILES = True
# Additional static files root
WHITENOISE_ROOT = os.path.join(BASE_DIR, 'public')
# Custom MIME types
WHITENOISE_MIMETYPES = {
'.myext': 'application/my-type',
}# utils.py
def add_custom_headers(headers, path, url):
"""Add custom headers to static files."""
if url.endswith('.js'):
headers['X-Content-Type-Options'] = 'nosniff'
headers['Referrer-Policy'] = 'strict-origin-when-cross-origin'
if url.startswith('/admin/'):
headers['X-Frame-Options'] = 'DENY'
# settings.py
WHITENOISE_ADD_HEADERS_FUNCTION = 'myapp.utils.add_custom_headers'WhiteNoise includes a management command that disables Django's static file serving:
# Add to INSTALLED_APPS
INSTALLED_APPS = [
# ... other apps
'whitenoise.runserver_nostatic',
]This automatically enables the --nostatic flag for runserver, ensuring WhiteNoise handles static files in development.
WhiteNoise includes a custom Django management command that disables Django's built-in static file serving.
class Command(BaseCommand):
"""
Custom runserver command that disables Django's static file serving.
Located in whitenoise.runserver_nostatic.management.commands.runserver,
this command extends Django's runserver to automatically add the --nostatic
flag, ensuring WhiteNoise handles all static file serving.
"""
def add_arguments(self, parser):
"""
Add command line arguments.
Extends the base runserver arguments while automatically
enabling the --nostatic flag to disable Django's static serving.
"""
def get_handler(self, *args, **options):
"""
Get WSGI handler with static file serving disabled.
Returns:
- WSGIHandler: Django WSGI handler without static file middleware
"""WhiteNoise automatically configures itself from Django settings:
| Setting | Default | Description |
|---|---|---|
WHITENOISE_AUTOREFRESH | DEBUG | Re-scan files on each request |
WHITENOISE_MAX_AGE | 60 (0 if DEBUG) | Cache-Control max-age in seconds |
WHITENOISE_ALLOW_ALL_ORIGINS | True | Add CORS * header |
WHITENOISE_CHARSET | 'utf-8' | Default charset for text files |
WHITENOISE_MIMETYPES | None | Additional MIME type mappings |
WHITENOISE_ADD_HEADERS_FUNCTION | None | Custom header function |
WHITENOISE_INDEX_FILE | None | Index file name |
WHITENOISE_IMMUTABLE_FILE_TEST | None | Immutable file test |
WHITENOISE_USE_FINDERS | DEBUG | Use Django staticfiles finders |
WHITENOISE_STATIC_PREFIX | Derived from STATIC_URL | Static files URL prefix |
WHITENOISE_ROOT | None | Additional static files directory |
WHITENOISE_SKIP_COMPRESS_EXTENSIONS | Built-in list | Extensions to skip compression |
WHITENOISE_KEEP_ONLY_HASHED_FILES | False | Remove non-hashed files |
WHITENOISE_MANIFEST_STRICT | None | Override manifest strict mode |
from typing import Any, Iterator, Union
from django.http import HttpRequest, HttpResponse
from django.contrib.staticfiles.storage import StaticFilesStorage, ManifestStaticFilesStorage
# Django middleware get_response type
GetResponseCallable = Callable[[HttpRequest], HttpResponse]
# Post-process iterator type
PostProcessIterator = Iterator[Union[tuple[str, str, bool], tuple[str, None, RuntimeError]]]Install with Tessl CLI
npx tessl i tessl/pypi-whitenoise