ASGI static file server — like Whitenoise, but for ASGI
Project description
whitesnout
WhiteSnout is an ASGI static file server for Python — like Whitenoise, but built for ASGI frameworks (FastAPI, Starlette, Django, etc.). It serves static files with minimal memory overhead, streaming content in chunks and leveraging pre-compressed assets. A Rust extension (PyO3) accelerates the hot path transparently.
Quick start
from whitesnout import WhiteSnout
# Standalone static file server
app = WhiteSnout(directory="./static")
Serve with any ASGI server:
$ uvicorn myapp:app
With FastAPI
from fastapi import FastAPI
from whitesnout import WhiteSnout
api = FastAPI()
@api.get("/api")
def read_root():
return {"hello": "world"}
app = WhiteSnout(api, directory="static")
With Django
# asgi.py
import os
from django.core.asgi import get_asgi_application
from whitesnout import WhiteSnout
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
django_app = get_asgi_application()
application = WhiteSnout(django_app, directory="static")
Installation
$ uv add whitesnout
Or with pip:
$ pip install whitesnout
Requires Python ≥ 3.10.
The compress CLI needs Brotli:
$ uv add 'whitesnout[compress]'
Pre-compressing assets
Generate .gz and .br variants for all files in a directory:
$ python -m whitesnout compress static/
Compressed: 42 gzip, 42 brotli
This is a build-time step — at runtime WhiteSnout serves the pre-compressed files directly with zero CPU overhead.
Configuration
All options can be passed as keyword arguments to WhiteSnout:
| Option | Default | Description |
|---|---|---|
app |
None |
Inner ASGI app to fall through to when a file is not found |
directory |
"static" |
Root directory to serve files from |
index_file |
"index.html" |
File to serve for directory requests |
cache_max_age |
3600 |
max-age in Cache-Control for regular files |
immutable_max_age |
31536000 |
max-age for hashed files (1 year) |
immutable_pattern |
r"\.[a-f0-9]{8,}\." |
Regex to detect hashed filenames (e.g. styles.a1b2c3d4.css) |
chunk_size |
65536 |
Stream chunk size in bytes (64 KB) |
charset |
"utf-8" |
Charset for text-based content types |
brotli |
True |
Look for .br pre-compressed variants |
gzip |
True |
Look for .gz pre-compressed variants |
max_cache_size |
100 |
Max entries in the LRU stat cache |
app = WhiteSnout(
app=my_asgi_app,
directory="public",
cache_max_age=86400,
immutable_max_age=31536000,
chunk_size=131072,
)
Features
- ASGI-native — middleware or standalone, works with any ASGI framework
- Streaming — files are served in configurable chunks (64 KB by default), never loaded entirely into memory
- Zero-copy pre-compression — serves pre-existing
.gzand.brfiles with automaticAccept-Encodingnegotiation; brotli preferred over gzip - Powerful caching —
ETag,Last-Modified,Cache-Controlheaders; 304 Not Modified responses for conditional requests - Immutable cache — detects hashed filenames (e.g.
app.abc12345.js) and appliesCache-Control: public, immutable, max-age=31536000 - Index files —
index.htmlserved automatically for directory paths - Clean URLs —
/dirredirects to/dir/(301 Moved Permanently) - Path traversal protection — resolved paths are verified to stay within the root directory
- Low overhead — LRU cache for file stats reduces
stat()syscalls; no dependency bloat - MIME types — content-type detection for 30+ file extensions, with automatic charset for text types
- Compress CLI —
python -m whitesnout compress <directory>generates pre-compressed.gzand.brfiles as a build step - Rust extension —
whitesnout._rsspeeds up the LRU cache transparently; pure Python fallback when unavailable - Multi-platform wheels — pre-built for Linux (x86_64, arm64), macOS (x86_64, arm64), and Windows (amd64)
Architecture
whitesnout/
├── main.py # ASGI middleware (always Python)
├── file_handler.py # Path resolution, compression negotiation
├── response.py # Header building, chunked streaming, 304
├── cache.py # LRU cache (Python → falls back to Rust)
├── config.py # Configuration dataclass
├── utils.py # MIME type table, helpers
├── cli.py # CLI entry point
├── compress.py # Compression logic
└── py.typed
whitesnout._rs # Compiled Rust extension (PyO3)
├── LRUCache # Rust implementation, auto fallback to Python
The core logic consists of pure functions designed for gradual migration to Rust. The ASGI integration layer (main.py) stays in Python forever — it is the thin touchpoint with the ASGI protocol.
Development
With Docker (recommended)
$ make build # Build Docker image + compile Rust + install deps
$ make test # Run test suite inside container
$ make shell # Open interactive shell in container
$ make release # Build release wheel
Requires Docker. The image is based on rust:slim-trixie with Python, uv, and maturin pre-installed.
Without Docker
$ uv sync --dev # Install Python deps + build Rust extension
$ uv run pytest -v # Run tests
$ maturin develop --uv # Rebuild Rust extension only
Requires Rust (via rustup) and maturin (cargo install maturin).
CHANGELOG
See CHANGELOG.md for the full release history.
License
MIT — see LICENSE for the full text.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file whitesnout-0.1.0.tar.gz.
File metadata
- Download URL: whitesnout-0.1.0.tar.gz
- Upload date:
- Size: 29.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d1c275d72ec06a02451f732b7b6c493414cc63415af61a661373c1ae1689aa85
|
|
| MD5 |
0e1142c483a4cc1c6a8c7e5992349c5a
|
|
| BLAKE2b-256 |
622d11e0248943afbc63e5bdd27384991253ff26265c35dd5283b1fcf360fbb8
|
Provenance
The following attestation bundles were made for whitesnout-0.1.0.tar.gz:
Publisher:
publish.yml on rroblf01/whitesnout
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
whitesnout-0.1.0.tar.gz -
Subject digest:
d1c275d72ec06a02451f732b7b6c493414cc63415af61a661373c1ae1689aa85 - Sigstore transparency entry: 1591039518
- Sigstore integration time:
-
Permalink:
rroblf01/whitesnout@2f4cacd40ea64360d0688959cc95fb497600faaf -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/rroblf01
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2f4cacd40ea64360d0688959cc95fb497600faaf -
Trigger Event:
push
-
Statement type:
File details
Details for the file whitesnout-0.1.0-cp310-abi3-win_amd64.whl.
File metadata
- Download URL: whitesnout-0.1.0-cp310-abi3-win_amd64.whl
- Upload date:
- Size: 141.1 kB
- Tags: CPython 3.10+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eef746698b4623989495e10c6ce565cfb2014c651f89d0b967b0115e0c6e22c6
|
|
| MD5 |
4c0ec05752a73f892a171d6ed91ef9e3
|
|
| BLAKE2b-256 |
7da2976c99dfa81b87f5ac709d13f5f6a699f7695296422268582650ef91470a
|
Provenance
The following attestation bundles were made for whitesnout-0.1.0-cp310-abi3-win_amd64.whl:
Publisher:
publish.yml on rroblf01/whitesnout
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
whitesnout-0.1.0-cp310-abi3-win_amd64.whl -
Subject digest:
eef746698b4623989495e10c6ce565cfb2014c651f89d0b967b0115e0c6e22c6 - Sigstore transparency entry: 1591039571
- Sigstore integration time:
-
Permalink:
rroblf01/whitesnout@2f4cacd40ea64360d0688959cc95fb497600faaf -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/rroblf01
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2f4cacd40ea64360d0688959cc95fb497600faaf -
Trigger Event:
push
-
Statement type:
File details
Details for the file whitesnout-0.1.0-cp310-abi3-musllinux_1_2_x86_64.whl.
File metadata
- Download URL: whitesnout-0.1.0-cp310-abi3-musllinux_1_2_x86_64.whl
- Upload date:
- Size: 347.6 kB
- Tags: CPython 3.10+, musllinux: musl 1.2+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cbb63784a571c31c83eb7a5285be4e390d455449ece7b38ba195b3f471b71b28
|
|
| MD5 |
b3ca2980e911be5e4c492956ce0db443
|
|
| BLAKE2b-256 |
880989030c6a23c8954b45212c6dc2ab3f053d92f4e3ab31ac7708f186909edc
|
Provenance
The following attestation bundles were made for whitesnout-0.1.0-cp310-abi3-musllinux_1_2_x86_64.whl:
Publisher:
publish.yml on rroblf01/whitesnout
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
whitesnout-0.1.0-cp310-abi3-musllinux_1_2_x86_64.whl -
Subject digest:
cbb63784a571c31c83eb7a5285be4e390d455449ece7b38ba195b3f471b71b28 - Sigstore transparency entry: 1591039587
- Sigstore integration time:
-
Permalink:
rroblf01/whitesnout@2f4cacd40ea64360d0688959cc95fb497600faaf -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/rroblf01
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2f4cacd40ea64360d0688959cc95fb497600faaf -
Trigger Event:
push
-
Statement type:
File details
Details for the file whitesnout-0.1.0-cp310-abi3-musllinux_1_2_aarch64.whl.
File metadata
- Download URL: whitesnout-0.1.0-cp310-abi3-musllinux_1_2_aarch64.whl
- Upload date:
- Size: 334.9 kB
- Tags: CPython 3.10+, musllinux: musl 1.2+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
78ed0ea09408bec6088803638bf75b558a4b3c64beed7b6bca957502dbd7b9ac
|
|
| MD5 |
363f38dc93fd53210e816110df2d7e27
|
|
| BLAKE2b-256 |
434b73d3a28db6d503a21bd1f3a419b628b41f1077cb1e118b93d0d4e665eec2
|
Provenance
The following attestation bundles were made for whitesnout-0.1.0-cp310-abi3-musllinux_1_2_aarch64.whl:
Publisher:
publish.yml on rroblf01/whitesnout
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
whitesnout-0.1.0-cp310-abi3-musllinux_1_2_aarch64.whl -
Subject digest:
78ed0ea09408bec6088803638bf75b558a4b3c64beed7b6bca957502dbd7b9ac - Sigstore transparency entry: 1591039527
- Sigstore integration time:
-
Permalink:
rroblf01/whitesnout@2f4cacd40ea64360d0688959cc95fb497600faaf -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/rroblf01
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2f4cacd40ea64360d0688959cc95fb497600faaf -
Trigger Event:
push
-
Statement type:
File details
Details for the file whitesnout-0.1.0-cp310-abi3-manylinux_2_28_x86_64.whl.
File metadata
- Download URL: whitesnout-0.1.0-cp310-abi3-manylinux_2_28_x86_64.whl
- Upload date:
- Size: 274.6 kB
- Tags: CPython 3.10+, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8639d504d4775f75e0322cbb0463a85569fc7349736567f25d777e411f8b26cf
|
|
| MD5 |
cb64128abf5b0c1901b370c35b4c7121
|
|
| BLAKE2b-256 |
944bf827fa46e9e1ea4df90cda0c89fcd95c883e2463068a357a955f2d813442
|
Provenance
The following attestation bundles were made for whitesnout-0.1.0-cp310-abi3-manylinux_2_28_x86_64.whl:
Publisher:
publish.yml on rroblf01/whitesnout
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
whitesnout-0.1.0-cp310-abi3-manylinux_2_28_x86_64.whl -
Subject digest:
8639d504d4775f75e0322cbb0463a85569fc7349736567f25d777e411f8b26cf - Sigstore transparency entry: 1591039563
- Sigstore integration time:
-
Permalink:
rroblf01/whitesnout@2f4cacd40ea64360d0688959cc95fb497600faaf -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/rroblf01
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2f4cacd40ea64360d0688959cc95fb497600faaf -
Trigger Event:
push
-
Statement type:
File details
Details for the file whitesnout-0.1.0-cp310-abi3-manylinux_2_28_aarch64.whl.
File metadata
- Download URL: whitesnout-0.1.0-cp310-abi3-manylinux_2_28_aarch64.whl
- Upload date:
- Size: 268.7 kB
- Tags: CPython 3.10+, manylinux: glibc 2.28+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
de33ce5097ece6758d8fa8316ef64e4c2e06dbc7efb1c1809c9987fc388e6f2b
|
|
| MD5 |
4efd9f25e00a418b8828ddb8b1aca263
|
|
| BLAKE2b-256 |
c3287009800812f1df012487810d171abee63a56496abbf2b76010d61581fef1
|
Provenance
The following attestation bundles were made for whitesnout-0.1.0-cp310-abi3-manylinux_2_28_aarch64.whl:
Publisher:
publish.yml on rroblf01/whitesnout
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
whitesnout-0.1.0-cp310-abi3-manylinux_2_28_aarch64.whl -
Subject digest:
de33ce5097ece6758d8fa8316ef64e4c2e06dbc7efb1c1809c9987fc388e6f2b - Sigstore transparency entry: 1591039582
- Sigstore integration time:
-
Permalink:
rroblf01/whitesnout@2f4cacd40ea64360d0688959cc95fb497600faaf -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/rroblf01
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2f4cacd40ea64360d0688959cc95fb497600faaf -
Trigger Event:
push
-
Statement type:
File details
Details for the file whitesnout-0.1.0-cp310-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: whitesnout-0.1.0-cp310-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 239.5 kB
- Tags: CPython 3.10+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
570b4b1f7becbdb9620c3faedf298a1029889a31c0884eb784b7d46b11c3a996
|
|
| MD5 |
22aa59ba35b9790722709a4361c2fea8
|
|
| BLAKE2b-256 |
8c3995de590270520efbd8ece2f32bbedfd3cdb5db2364283923ba98f8d0ffbd
|
Provenance
The following attestation bundles were made for whitesnout-0.1.0-cp310-abi3-macosx_11_0_arm64.whl:
Publisher:
publish.yml on rroblf01/whitesnout
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
whitesnout-0.1.0-cp310-abi3-macosx_11_0_arm64.whl -
Subject digest:
570b4b1f7becbdb9620c3faedf298a1029889a31c0884eb784b7d46b11c3a996 - Sigstore transparency entry: 1591039556
- Sigstore integration time:
-
Permalink:
rroblf01/whitesnout@2f4cacd40ea64360d0688959cc95fb497600faaf -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/rroblf01
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2f4cacd40ea64360d0688959cc95fb497600faaf -
Trigger Event:
push
-
Statement type:
File details
Details for the file whitesnout-0.1.0-cp310-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: whitesnout-0.1.0-cp310-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 243.4 kB
- Tags: CPython 3.10+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
04cad1d191c85b1d59864e5a3b8d8db6c5ca68bb6d85e665dff7eb823cae3609
|
|
| MD5 |
8a294671b37a2456b6daf0f5925a9a8a
|
|
| BLAKE2b-256 |
4ed8501ffc1e313a4eab3971654492df35db5477f815cec82b1d018d9e62e495
|
Provenance
The following attestation bundles were made for whitesnout-0.1.0-cp310-abi3-macosx_10_12_x86_64.whl:
Publisher:
publish.yml on rroblf01/whitesnout
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
whitesnout-0.1.0-cp310-abi3-macosx_10_12_x86_64.whl -
Subject digest:
04cad1d191c85b1d59864e5a3b8d8db6c5ca68bb6d85e665dff7eb823cae3609 - Sigstore transparency entry: 1591039542
- Sigstore integration time:
-
Permalink:
rroblf01/whitesnout@2f4cacd40ea64360d0688959cc95fb497600faaf -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/rroblf01
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2f4cacd40ea64360d0688959cc95fb497600faaf -
Trigger Event:
push
-
Statement type: