Static File Serving

Chunked file serving with pre-compression and ETags

3 min read 566 words

Pounce provides high-performance static file serving with pre-compressed file support, ETags, range requests, and automatic content negotiation.

Quick Start

from pounce import ServerConfig

config = ServerConfig(
    static_files={
        "/static": "./public",      # Serve ./public at /static
        "/assets": "./dist/assets", # Multiple mappings supported
    },
    static_precompressed=True,      # Serve .gz/.zst if available
    static_cache_control="public, max-age=3600",
)

Access files at:

  • http://localhost:8000/static/style.css./public/style.css
  • http://localhost:8000/assets/app.js./dist/assets/app.js

Features

Pre-Compressed Files

Automatically serves.zst or .gzfiles when available and the client accepts them:

  • style.css.zstserved for zstd-supporting clients
  • app.js.gzserved for gzip-only clients
  • Falls back to uncompressed file

ETag Support

Generates ETags based on file mtime and size for efficient caching:

  • Client sendsIf-None-Matchheader
  • Server responds with304 Not Modifiedif ETag matches
  • Saves bandwidth and reduces server load

Range Requests

Full support for byte-range requests (RFC 7233):

  • Video streaming
  • Resume downloads
  • Seeking in audio files

Configuration

static_files

Map URL paths to filesystem directories:

config = ServerConfig(
    static_files={
        "/": "./public",              # Root serving
        "/static": "./assets",        # Nested path
        "/downloads": "/var/files",   # Absolute paths OK
    }
)

static_precompressed

Enable pre-compressed file serving:

config = ServerConfig(
    static_precompressed=True,  # Default: True
)

Pounce checks for these variants in order:

  1. .zst(zstd) — fast decompression, good compression
  2. .gz(gzip) — universal compatibility
  3. Original file

static_cache_control

Set Cache-Control header for static files:

config = ServerConfig(
    static_cache_control="public, max-age=86400",  # 24 hours
)

Recommendations:

  • Immutable assets:public, max-age=31536000, immutable
  • Frequently updated:public, max-age=3600(1 hour)
  • Private content:private, max-age=0, must-revalidate

static_index_file

Serve index files for directory requests:

config = ServerConfig(
    static_index_file="index.html",  # Default
)

Makes / serve ./public/index.htmlautomatically.

Control symlink following:

config = ServerConfig(
    static_follow_symlinks=False,  # Default: False (security)
)

Security: Keep disabled in production to prevent directory traversal attacks.

Use Cases

Single Page Applications (SPAs)

Serve built assets and fallback to index.html:

config = ServerConfig(
    static_files={"/": "./dist"},
    static_index_file="index.html",
    static_cache_control="public, max-age=31536000, immutable",
)

CDN Origin Server

High-performance static file origin with aggressive caching:

config = ServerConfig(
    static_files={"/assets": "./cdn"},
    static_precompressed=True,
    static_cache_control="public, max-age=31536000, immutable",
)

Documentation Sites

Serve pre-built documentation (generated by Bengal, MkDocs, etc.):

config = ServerConfig(
    static_files={"/": "./site"},
    static_index_file="index.html",
)

Performance

Pre-Compressed Files

Pre-compressing files saves CPU and improves TTFB:

  • zstd level 19: Fast decompression, good compression
  • gzip level 9: Universal compatibility
  • Build step: Compress once, serve many times

Example build script:

find ./public -type f \( -name "*.html" -o -name "*.css" -o -name "*.js" \) -exec zstd -k {} \;
find ./public -type f \( -name "*.html" -o -name "*.css" -o -name "*.js" \) -exec gzip -k {} \;

Security

Path Traversal Protection

Pounce validates all paths to prevent../attacks:

  • Requests like/static/../../../etc/passwdare rejected
  • Normalized paths checked against allowed directories

Symlinks disabled by default (static_follow_symlinks=False):

  • Prevents escaping static directory via symlinks
  • Enable only if you control all symlink targets

MIME Type Safety

Correct Content-Type headers prevent XSS:

  • .htmltext/html
  • .jsapplication/javascript
  • .csstext/css
  • Unknown →application/octet-stream

See Also