Statik is a high-performance static file server designed for ASGI applications. It provides efficient file serving capabilities with minimal dependencies, making it ideal for production environments where performance and security are critical.
- High Performance: Optimized for serving static files with minimal overhead and maximum throughput
- Minimal Dependencies: Core functionality requires only
anyio
andtyping-extensions
- Security Features: Built-in protection against path traversal and configurable authentication
- Compression Support: Automatic gzip and deflate compression for supported content types
- Caching System: ETag support and configurable cache control headers
- Streaming Support: Efficient large file handling with configurable chunk sizes
- SPA Mode: Built-in Single Page Application support
- Directory Listings: Optional HTML/JSON directory listings with authentication
- Range Requests: Support for partial content and resume downloads
pip install asgi_statik
from statik import StaticFiles
# Basic static file server
app = StaticFiles(directory="static")
# Run with any ASGI server
# Example with uvicorn:
# uvicorn myapp:app
from statik import StaticFiles, StaticFilesConfig
# Simple static file server
app = StaticFiles(directory="static")
# Advanced configuration
config = StaticFilesConfig(
directory="static",
chunk_size=32 * 1024, # 32KB chunks
follow_symlinks=False,
check_dir=True
)
app = StaticFiles(config=config)
config = StaticFilesConfig(
directory="static",
allowed_methods=["GET", "HEAD"],
directory_listing_auth={
"admin": "secret" # Basic auth for directory listing
},
follow_symlinks=False,
check_dir=True
)
config = StaticFilesConfig(
directory="static",
enable_compression=True,
compression_min_size=1024, # 1KB minimum
compression_types=[
"text/",
"application/javascript",
"application/json",
"application/xml"
],
cache_control="public, max-age=3600",
cache_max_age=3600
)
config = StaticFilesConfig(
directory="static",
spa_mode=True,
index_files=["index.html"],
cache_control="public, max-age=3600"
)
from fastapi import FastAPI
from statik import StaticFiles, StaticFilesConfig
app = FastAPI()
# Basic static files
app.mount("/static", StaticFiles(directory="static"))
# Advanced configuration
config = StaticFilesConfig(
directory="static/assets",
enable_compression=True,
compression_min_size=1024,
cache_control="public, max-age=3600",
spa_mode=False
)
# Mount multiple static directories
app.mount("/assets", StaticFiles(config=config))
app.mount("/public", StaticFiles(directory="public"))
# Protected static files with basic auth
protected_config = StaticFilesConfig(
directory="protected",
directory_listing_auth={
"admin": "secret123"
},
allow_directory_listing=True
)
app.mount("/protected", StaticFiles(config=protected_config))
# Example route using static files
@app.get("/")
async def read_root():
return {"message": "Static files are served at /static, /assets, and /protected"}
from starlette.applications import Starlette
from starlette.routing import Mount
from starlette.responses import JSONResponse
from statik import StaticFiles, StaticFilesConfig
routes = [
# Basic static files
Mount("/static", StaticFiles(directory="static")),
# SPA configuration
Mount("/app", StaticFiles(
config=StaticFilesConfig(
directory="spa",
spa_mode=True,
index_files=["index.html"],
cache_control="public, max-age=3600"
)
)),
# Media files with compression
Mount("/media", StaticFiles(
config=StaticFilesConfig(
directory="media",
enable_compression=True,
compression_types=[
"image/svg+xml",
"application/json",
"text/css",
"application/javascript"
]
)
))
]
app = Starlette(routes=routes)
@app.route("/")
async def homepage(request):
return JSONResponse({
"static_routes": [
"/static",
"/app",
"/media"
]
})
from nexios import NexiosApp
from statik import StaticFiles, StaticFilesConfig
app = NexiosApp()
# Basic static file serving
app.register(StaticFiles(directory="static"),"/static")
@app.get("/api/status")
async def status(req, res):
return {
"status": "running",
"static_mounts": [
"/", # SPA
"/docs",
"/media"
]
}
# Run the application
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
from fastapi import FastAPI
from statik import StaticFiles, StaticFilesConfig
app = FastAPI()
# Structure:
# /static
# /css
# /js
# /images
# /uploads
# /public
# Serve different directories with different configurations
app.mount("/static", StaticFiles(
config=StaticFilesConfig(
directory="static",
enable_compression=True,
cache_control="public, max-age=3600"
)
))
app.mount("/uploads", StaticFiles(
config=StaticFilesConfig(
directory="uploads",
chunk_size=256 * 1024, # Larger chunks for uploads
enable_compression=False # Don't compress already compressed files
)
))
app.mount("/public", StaticFiles(directory="public"))
from fastapi import FastAPI
from statik import StaticFiles, StaticFilesConfig
app = FastAPI()
# Serve SPA from /app
spa_config = StaticFilesConfig(
directory="frontend/dist",
spa_mode=True,
enable_compression=True,
cache_control="public, max-age=3600"
)
app.mount("/app", StaticFiles(config=spa_config))
# API routes
@app.get("/api/data")
async def get_data():
return {"data": "example"}
# Serve API documentation
docs_config = StaticFilesConfig(
directory="docs",
allow_directory_listing=True
)
app.mount("/docs", StaticFiles(config=docs_config))
from fastapi import FastAPI
from statik import StaticFiles, StaticFilesConfig
app = FastAPI()
# Protected media files with authentication
media_config = StaticFilesConfig(
directory="media",
directory_listing_auth={
"admin": "secure_password",
"user": "user_password"
},
allow_directory_listing=True,
chunk_size=128 * 1024,
compression_types=[
"image/svg+xml",
"text/plain",
"application/json"
]
)
app.mount("/media", StaticFiles(config=media_config))
# Optional: Add route to check auth status
@app.get("/media-status")
async def media_status():
return {
"status": "available",
"auth_required": True
}
These examples demonstrate common use cases and best practices for integrating Statik with various frameworks. Each example includes appropriate configuration for the specific use case, such as:
- Compression settings for appropriate file types
- Caching strategies
- Authentication for protected resources
- SPA mode configuration
- Large file handling
- Multiple directory mounting
Choose and adapt these examples based on your specific requirements.
Option | Type | Default | Description |
---|---|---|---|
directory |
str|Path |
Required | Base directory for serving files |
check_dir |
bool |
True |
Verify directory exists on startup |
follow_symlinks |
bool |
False |
Allow following symbolic links |
allow_directory_listing |
bool |
False |
Enable directory listing feature |
directory_listing_auth |
dict |
None |
Basic auth credentials for directory listing |
allowed_methods |
list |
["GET", "HEAD"] |
Allowed HTTP methods |
cache_control |
str |
None |
Cache-Control header value |
cache_max_age |
int |
3600 |
Cache max age in seconds |
enable_compression |
bool |
False |
Enable compression support |
compression_min_size |
int |
1024 |
Minimum file size for compression |
compression_types |
list |
See below | MIME types to compress |
chunk_size |
int |
64 * 1024 |
File streaming chunk size |
spa_mode |
bool |
False |
Enable SPA mode |
index_files |
list |
["index.html"] |
List of index file names |
DEFAULT_COMPRESSION_TYPES = [
"text/",
"application/javascript",
"application/json",
"application/xml",
"application/wasm"
]
- Larger chunks (128KB+): Better for high-bandwidth scenarios
- Smaller chunks (32KB): Better for memory-constrained environments
- Default (64KB): Good balance for most use cases
config = StaticFilesConfig(
directory="static",
chunk_size=128 * 1024 # 128KB chunks
)
- Enable compression only for text-based files
- Set appropriate minimum file size (typically 1KB+)
- Configure supported MIME types carefully
- Set appropriate cache-control headers
- Enable ETag support for client-side caching
- Configure cache max age based on content update frequency
- Keep
follow_symlinks=False
unless specifically needed - Implement authentication for sensitive directories
- Use allowed_methods to restrict HTTP methods
- Validate file paths against directory traversal
The server handles various error conditions with appropriate HTTP status codes:
- 404 Not Found: File or directory not found
- 403 Forbidden: Access denied or directory traversal attempt
- 401 Unauthorized: Failed authentication
- 405 Method Not Allowed: Invalid HTTP method
- 500 Internal Server Error: Unexpected server errors
- Fork the repository
- Create a feature branch (
git checkout -b feature/improvement
) - Commit your changes (
git commit -am 'Add new feature'
) - Push to the branch (
git push origin feature/improvement
) - Create a Pull Request
For major changes, please open an issue first to discuss the proposed changes.
This project is licensed under the MIT License. See the LICENSE file for details.