Skip to main content

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 .gz and .br files with automatic Accept-Encoding negotiation; brotli preferred over gzip
  • Powerful cachingETag, Last-Modified, Cache-Control headers; 304 Not Modified responses for conditional requests
  • Immutable cache — detects hashed filenames (e.g. app.abc12345.js) and applies Cache-Control: public, immutable, max-age=31536000
  • Index filesindex.html served automatically for directory paths
  • Clean URLs/dir redirects 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 CLIpython -m whitesnout compress <directory> generates pre-compressed .gz and .br files as a build step
  • Rust extensionwhitesnout._rs speeds 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

whitesnout-0.1.0.tar.gz (29.9 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

whitesnout-0.1.0-cp310-abi3-win_amd64.whl (141.1 kB view details)

Uploaded CPython 3.10+Windows x86-64

whitesnout-0.1.0-cp310-abi3-musllinux_1_2_x86_64.whl (347.6 kB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ x86-64

whitesnout-0.1.0-cp310-abi3-musllinux_1_2_aarch64.whl (334.9 kB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ ARM64

whitesnout-0.1.0-cp310-abi3-manylinux_2_28_x86_64.whl (274.6 kB view details)

Uploaded CPython 3.10+manylinux: glibc 2.28+ x86-64

whitesnout-0.1.0-cp310-abi3-manylinux_2_28_aarch64.whl (268.7 kB view details)

Uploaded CPython 3.10+manylinux: glibc 2.28+ ARM64

whitesnout-0.1.0-cp310-abi3-macosx_11_0_arm64.whl (239.5 kB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

whitesnout-0.1.0-cp310-abi3-macosx_10_12_x86_64.whl (243.4 kB view details)

Uploaded CPython 3.10+macOS 10.12+ x86-64

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

Hashes for whitesnout-0.1.0.tar.gz
Algorithm Hash digest
SHA256 d1c275d72ec06a02451f732b7b6c493414cc63415af61a661373c1ae1689aa85
MD5 0e1142c483a4cc1c6a8c7e5992349c5a
BLAKE2b-256 622d11e0248943afbc63e5bdd27384991253ff26265c35dd5283b1fcf360fbb8

See more details on using hashes here.

Provenance

The following attestation bundles were made for whitesnout-0.1.0.tar.gz:

Publisher: publish.yml on rroblf01/whitesnout

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for whitesnout-0.1.0-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 eef746698b4623989495e10c6ce565cfb2014c651f89d0b967b0115e0c6e22c6
MD5 4c0ec05752a73f892a171d6ed91ef9e3
BLAKE2b-256 7da2976c99dfa81b87f5ac709d13f5f6a699f7695296422268582650ef91470a

See more details on using hashes here.

Provenance

The following attestation bundles were made for whitesnout-0.1.0-cp310-abi3-win_amd64.whl:

Publisher: publish.yml on rroblf01/whitesnout

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file whitesnout-0.1.0-cp310-abi3-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for whitesnout-0.1.0-cp310-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 cbb63784a571c31c83eb7a5285be4e390d455449ece7b38ba195b3f471b71b28
MD5 b3ca2980e911be5e4c492956ce0db443
BLAKE2b-256 880989030c6a23c8954b45212c6dc2ab3f053d92f4e3ab31ac7708f186909edc

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file whitesnout-0.1.0-cp310-abi3-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for whitesnout-0.1.0-cp310-abi3-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 78ed0ea09408bec6088803638bf75b558a4b3c64beed7b6bca957502dbd7b9ac
MD5 363f38dc93fd53210e816110df2d7e27
BLAKE2b-256 434b73d3a28db6d503a21bd1f3a419b628b41f1077cb1e118b93d0d4e665eec2

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file whitesnout-0.1.0-cp310-abi3-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for whitesnout-0.1.0-cp310-abi3-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 8639d504d4775f75e0322cbb0463a85569fc7349736567f25d777e411f8b26cf
MD5 cb64128abf5b0c1901b370c35b4c7121
BLAKE2b-256 944bf827fa46e9e1ea4df90cda0c89fcd95c883e2463068a357a955f2d813442

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file whitesnout-0.1.0-cp310-abi3-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for whitesnout-0.1.0-cp310-abi3-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 de33ce5097ece6758d8fa8316ef64e4c2e06dbc7efb1c1809c9987fc388e6f2b
MD5 4efd9f25e00a418b8828ddb8b1aca263
BLAKE2b-256 c3287009800812f1df012487810d171abee63a56496abbf2b76010d61581fef1

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file whitesnout-0.1.0-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for whitesnout-0.1.0-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 570b4b1f7becbdb9620c3faedf298a1029889a31c0884eb784b7d46b11c3a996
MD5 22aa59ba35b9790722709a4361c2fea8
BLAKE2b-256 8c3995de590270520efbd8ece2f32bbedfd3cdb5db2364283923ba98f8d0ffbd

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file whitesnout-0.1.0-cp310-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for whitesnout-0.1.0-cp310-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 04cad1d191c85b1d59864e5a3b8d8db6c5ca68bb6d85e665dff7eb823cae3609
MD5 8a294671b37a2456b6daf0f5925a9a8a
BLAKE2b-256 4ed8501ffc1e313a4eab3971654492df35db5477f815cec82b1d018d9e62e495

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page