0
# Core WSGI Middleware
1
2
The WhiteNoise class provides the core static file serving functionality as WSGI middleware. It intercepts requests for static files and serves them directly, allowing your application to handle dynamic content while WhiteNoise efficiently serves static assets.
3
4
## Capabilities
5
6
### WhiteNoise Class
7
8
The main WSGI middleware class that serves static files with comprehensive HTTP feature support including compression, caching, range requests, and proper header handling.
9
10
```python { .api }
11
class WhiteNoise:
12
"""
13
WSGI middleware for serving static files.
14
15
Wraps a WSGI application to serve static files before passing requests
16
to the wrapped application. Supports compression, caching, and HTTP
17
best practices.
18
"""
19
20
def __init__(
21
self,
22
application,
23
root=None,
24
prefix=None,
25
*,
26
autorefresh: bool = False,
27
max_age: int | None = 60,
28
allow_all_origins: bool = True,
29
charset: str = "utf-8",
30
mimetypes: dict[str, str] | None = None,
31
add_headers_function: Callable[[Headers, str, str], None] | None = None,
32
index_file: str | bool | None = None,
33
immutable_file_test: Callable | str | None = None,
34
):
35
"""
36
Initialize WhiteNoise middleware.
37
38
Parameters:
39
- application: WSGI application to wrap
40
- root: Path to static files directory
41
- prefix: URL prefix for static files (e.g., '/static/')
42
- autorefresh: Re-scan filesystem on each request (development only)
43
- max_age: Cache-Control max-age in seconds (None disables caching)
44
- allow_all_origins: Add Access-Control-Allow-Origin: * header
45
- charset: Default charset for text files
46
- mimetypes: Additional MIME type mappings
47
- add_headers_function: Custom function to add headers
48
- index_file: Index file name (True for 'index.html')
49
- immutable_file_test: Function or regex to identify immutable files
50
"""
51
52
def __call__(self, environ, start_response):
53
"""
54
WSGI application interface.
55
56
Parameters:
57
- environ: WSGI environment dictionary
58
- start_response: WSGI start_response callable
59
60
Returns:
61
- WSGI response iterator
62
"""
63
64
def add_files(self, root, prefix=None):
65
"""
66
Add a directory of static files to serve.
67
68
Parameters:
69
- root: Absolute path to static files directory
70
- prefix: URL prefix for files (e.g., '/static/')
71
"""
72
73
@staticmethod
74
def serve(static_file, environ, start_response):
75
"""
76
Serve a static file (static method).
77
78
Parameters:
79
- static_file: StaticFile instance to serve
80
- environ: WSGI environment dictionary
81
- start_response: WSGI start_response callable
82
83
Returns:
84
- WSGI response iterator
85
"""
86
87
FOREVER: int # Cache duration constant (10 years in seconds)
88
```
89
90
### Usage Examples
91
92
#### Basic WSGI Integration
93
94
```python
95
from whitenoise import WhiteNoise
96
from my_app import application
97
98
# Wrap your WSGI application
99
application = WhiteNoise(application)
100
101
# Add static files with URL prefix
102
application.add_files('/var/www/static', prefix='/static/')
103
application.add_files('/var/www/media', prefix='/media/')
104
```
105
106
#### Development Configuration
107
108
```python
109
# Enable autorefresh for development (performance impact)
110
application = WhiteNoise(
111
application,
112
autorefresh=True,
113
max_age=0 # Disable caching for development
114
)
115
```
116
117
#### Production Configuration with Custom Headers
118
119
```python
120
def add_security_headers(headers, path, url):
121
"""Add security headers to static files."""
122
if url.endswith('.js'):
123
headers['X-Content-Type-Options'] = 'nosniff'
124
if url.endswith('.css'):
125
headers['X-Content-Type-Options'] = 'nosniff'
126
127
application = WhiteNoise(
128
application,
129
max_age=31536000, # 1 year cache for production
130
allow_all_origins=True,
131
add_headers_function=add_security_headers
132
)
133
```
134
135
#### Immutable File Detection
136
137
```python
138
import re
139
140
# Using regex pattern for versioned files
141
versioned_file_re = r'\.[0-9a-f]{8,32}\.'
142
143
application = WhiteNoise(
144
application,
145
immutable_file_test=versioned_file_re
146
)
147
148
# Using custom function
149
def is_immutable(path, url):
150
"""Check if file is immutable based on naming convention."""
151
return '.min.' in url or '.hash.' in url
152
153
application = WhiteNoise(
154
application,
155
immutable_file_test=is_immutable
156
)
157
```
158
159
### File Discovery and Caching
160
161
WhiteNoise operates in two modes for file discovery:
162
163
**Static Mode (Default)**: Files are scanned once at startup and stored in memory for fast lookup. Suitable for production environments.
164
165
**Autorefresh Mode**: Files are discovered on each request by scanning the filesystem. Only use for development as it has significant performance impact.
166
167
### HTTP Features
168
169
WhiteNoise implements comprehensive HTTP best practices:
170
171
- **Content Negotiation**: Automatically serves compressed variants (gzip, brotli) based on Accept-Encoding header
172
- **Conditional Requests**: Supports If-Modified-Since and If-None-Match headers for 304 responses
173
- **Range Requests**: Handles partial content requests for large files
174
- **CORS Support**: Configurable Cross-Origin Resource Sharing headers
175
- **Cache Headers**: Intelligent cache control with support for immutable files
176
177
### Static File Response System
178
179
Internal classes that handle HTTP responses and file serving, used by WhiteNoise for comprehensive static file delivery.
180
181
```python { .api }
182
class Response:
183
"""
184
HTTP response container for static file serving.
185
186
Represents the complete HTTP response including status, headers, and file content.
187
"""
188
189
def __init__(self, status, headers, file):
190
"""
191
Initialize HTTP response.
192
193
Parameters:
194
- status: HTTPStatus enum value
195
- headers: List of (name, value) header tuples
196
- file: File object or None for responses without body
197
"""
198
199
class StaticFile:
200
"""
201
Represents a static file with all its variants and metadata.
202
203
Handles compressed alternatives, conditional requests, range requests,
204
and proper HTTP header generation for efficient static file serving.
205
"""
206
207
def __init__(self, path, headers, encodings=None, stat_cache=None):
208
"""
209
Initialize static file representation.
210
211
Parameters:
212
- path: Absolute path to the file
213
- headers: List of (name, value) header tuples
214
- encodings: Dict mapping encodings to compressed file paths
215
- stat_cache: Optional stat cache for performance
216
"""
217
218
def get_response(self, method, request_headers):
219
"""
220
Generate HTTP response for the file.
221
222
Parameters:
223
- method: HTTP method (GET, HEAD, etc.)
224
- request_headers: Dict of request headers
225
226
Returns:
227
- Response: Complete HTTP response object
228
229
Handles conditional requests, range requests, content negotiation,
230
and proper HTTP status codes.
231
"""
232
233
class SlicedFile:
234
"""
235
File-like object for HTTP range requests.
236
237
Wraps a file to serve only a specific byte range, enabling
238
efficient partial content delivery for large files.
239
"""
240
241
def __init__(self, fileobj, start, end):
242
"""
243
Create sliced file for range request.
244
245
Parameters:
246
- fileobj: Source file object
247
- start: Starting byte position (inclusive)
248
- end: Ending byte position (inclusive)
249
"""
250
251
def read(self, size=-1):
252
"""
253
Read bytes respecting range limits.
254
255
Parameters:
256
- size: Maximum bytes to read (-1 for remaining)
257
258
Returns:
259
- bytes: File content within range limits
260
"""
261
262
class Redirect:
263
"""
264
HTTP redirect response for URL rewriting.
265
266
Used for index file handling and URL canonicalization.
267
"""
268
269
def __init__(self, location, headers=None):
270
"""
271
Create redirect response.
272
273
Parameters:
274
- location: Target URL for redirect
275
- headers: Optional additional headers
276
"""
277
278
def get_response(self, method, request_headers):
279
"""
280
Generate redirect response.
281
282
Parameters:
283
- method: HTTP method
284
- request_headers: Request headers dict
285
286
Returns:
287
- Response: 302 redirect response
288
"""
289
290
class FileEntry:
291
"""
292
File metadata container for stat caching.
293
294
Stores file path, size, and modification time for efficient
295
file system operations and HTTP header generation.
296
"""
297
298
def __init__(self, path, stat_cache=None):
299
"""
300
Create file entry with metadata.
301
302
Parameters:
303
- path: Absolute file path
304
- stat_cache: Optional stat cache for performance
305
"""
306
```
307
308
### Media Type Resolution
309
310
MIME type detection system for proper Content-Type header generation.
311
312
```python { .api }
313
class MediaTypes:
314
"""
315
MIME type resolver for static files.
316
317
Provides consistent media type detection across environments
318
using WhiteNoise's built-in type mappings with optional extensions.
319
"""
320
321
def __init__(self, *, extra_types: dict[str, str] | None = None):
322
"""
323
Initialize media type resolver.
324
325
Parameters:
326
- extra_types: Additional MIME type mappings to include
327
"""
328
329
def get_type(self, path: str) -> str:
330
"""
331
Determine MIME type for file path.
332
333
Parameters:
334
- path: File path to analyze
335
336
Returns:
337
- str: MIME type string (defaults to 'application/octet-stream')
338
339
Checks both filename and extension, with filename taking precedence
340
for special cases like 'apple-app-site-association'.
341
"""
342
```
343
344
### String Utilities
345
346
URL and path processing utilities for consistent handling across WhiteNoise.
347
348
```python { .api }
349
def decode_path_info(path_info):
350
"""
351
Decode URL path from WSGI PATH_INFO.
352
353
Parameters:
354
- path_info: Raw PATH_INFO string from WSGI environ
355
356
Returns:
357
- str: UTF-8 decoded path string
358
359
Handles UTF-8 URLs by undoing Python's implicit ISO-8859-1 decoding.
360
"""
361
362
def ensure_leading_trailing_slash(path):
363
"""
364
Normalize path to have leading and trailing slashes.
365
366
Parameters:
367
- path: Path string to normalize
368
369
Returns:
370
- str: Normalized path with leading/trailing slashes
371
372
Returns '/' for empty/None input, '/{path}/' for non-empty input.
373
"""
374
```
375
376
### Exception Classes
377
378
Specialized exceptions for static file handling and error reporting.
379
380
```python { .api }
381
class NotARegularFileError(Exception):
382
"""
383
Base exception for non-regular file encounters.
384
385
Raised when attempting to serve directories, devices, or other
386
non-regular file system objects.
387
"""
388
389
class MissingFileError(NotARegularFileError):
390
"""
391
Exception for missing or inaccessible files.
392
393
Raised when a requested file cannot be found or accessed.
394
"""
395
396
class IsDirectoryError(MissingFileError):
397
"""
398
Exception for directory access attempts.
399
400
Raised when attempting to serve a directory as a file.
401
"""
402
```
403
404
## Types
405
406
```python { .api }
407
from typing import Callable, Any
408
from wsgiref.headers import Headers
409
from http import HTTPStatus
410
411
# WSGI application type
412
WSGIApplication = Callable[[dict, Callable], Any]
413
414
# Custom header function type
415
AddHeadersFunction = Callable[[Headers, str, str], None]
416
417
# Immutable file test types
418
ImmutableFileTest = Callable[[str, str], bool]
419
ImmutableFilePattern = str
420
421
# HTTP response components
422
HTTPHeaders = list[tuple[str, str]]
423
RequestHeaders = dict[str, str]
424
```