Serving Static Files

Guide to serving static files (CSS, JavaScript, images, etc.).

Table of Contents


Basic Setup

StaticFilesMiddleware

Use StaticFilesMiddleware for serving static files.

use reinhardt::staticfiles::StaticFilesMiddleware;
use reinhardt::staticfiles::storage::StaticFilesConfig;

let config = StaticFilesConfig {
    static_root: "static".into(),
    static_url: "/static/".to_string(),
    staticfiles_dirs: vec![
        "app/static".into(),
        "vendor/static".into(),
    ],
    media_url: Some("/media/".to_string()),
};

let middleware = StaticFilesMiddleware::new(config);

Note: There are two StaticFilesConfig types in reinhardt::staticfiles:

  • storage::StaticFilesConfig - For storage configuration (static_root, static_url, staticfiles_dirs, media_url)
  • middleware::StaticFilesConfig - For middleware configuration (root_dir, url_prefix, spa_mode, etc.)

This example uses the storage version. The storage config is also re-exported at reinhardt::staticfiles::StaticFilesConfig.


Storage Backends

FileSystemStorage

Saves files to local filesystem.

use reinhardt::staticfiles::storage::FileSystemStorage;

let storage = FileSystemStorage::new("/var/www/static", "https://cdn.example.com/static");

// Save file
let url = storage.save("css/style.css", b"body { margin: 0; }").await?;

// Check if file exists
if storage.exists("css/style.css") {
    // Open file
    let content = storage.open("css/style.css").await?;
}

// Get URL
let url = storage.url("css/style.css");
// Returns: "https://cdn.example.com/static/css/style.css"

// Delete file
storage.delete("css/style.css").await?;

MemoryStorage

In-memory storage (for testing).

use reinhardt::staticfiles::storage::MemoryStorage;

let storage = MemoryStorage::new("/static/");

// Save file
let url = storage.save("test.txt", b"Hello, World!").await?;

// Get file
let content = storage.open("test.txt").await?;

S3Storage

Saves files to AWS S3 (requires s3 feature).

use reinhardt::staticfiles::storage::{S3Storage, S3Config};

async fn example() -> Result<(), Box<dyn std::error::Error>> {
let config = S3Config {
    bucket: "my-bucket".to_string(),
    region: "us-east-1".to_string(),
    key_prefix: Some("static/".to_string()),
    cdn_domain: Some("https://cdn.example.com".to_string()),
};

let storage = S3Storage::new(config);

// Save file
let url = storage.save("images/logo.png", image_bytes).await?;
// Returns: "https://cdn.example.com/static/images/logo.png"
Ok(())
}

AzureBlobStorage

Saves files to Azure Blob Storage (requires azure feature).

use reinhardt::staticfiles::storage::{AzureBlobStorage, AzureBlobConfig};

async fn example() -> Result<(), Box<dyn std::error::Error>> {
let config = AzureBlobConfig {
    connection_string: "...".to_string(),
    container: "static-files".to_string(),
    cdn_domain: Some("https://myaccount.blob.core.windows.net".to_string()),
};

let storage = AzureBlobStorage::new(config);
Ok(())
}

GcsStorage

Saves files to Google Cloud Storage (requires gcs feature).

use reinhardt::staticfiles::storage::{GcsStorage, GcsConfig};

async fn example() -> Result<(), Box<dyn std::error::Error>> {
let config = GcsConfig {
    bucket: "my-bucket".to_string(),
    key_prefix: Some("static/".to_string()),
    credentials_path: "/path/to/credentials.json".to_string(),
};

let storage = GcsStorage::new(config);
Ok(())
}

Development vs Production

Development

Use StaticFileHandler to serve local files directly.

use reinhardt::staticfiles::handler::StaticFileHandler;

let handler = StaticFileHandler::new("/path/to/static");

Production

Use HashedFileStorage for cache busting.

use reinhardt::staticfiles::storage::HashedFileStorage;

let storage = HashedFileStorage::new("/var/www/static", "https://cdn.example.com/static");

// Hash is automatically added on save
// "style.css" -> "style.a1b2c3d4e5f6.css"
let url = storage.save("style.css", css_bytes).await?;

ManifestStaticFilesStorage

Versioned files using manifest (similar to Django's ManifestStaticFilesStorage).

use reinhardt::staticfiles::storage::{ManifestStaticFilesStorage, Manifest};

let manifest = Manifest {
    version: "v1.0.0".to_string(),
    files: vec![
        ("css/style.css".to_string(), "css/style.v1.css".to_string()),
    ],
};

let storage = ManifestStaticFilesStorage::new(
    "/var/www/static",
    "https://cdn.example.com/static",
    manifest,
);

// Get versioned path from manifest
let url = storage.url("css/style.css");
// Returns: "https://cdn.example.com/static/css/style.v1.css"

Path Resolution

PathResolver

Resolves paths for static files.

use reinhardt::staticfiles::PathResolver;

let resolver = PathResolver::new(vec![
    "/app/static".into(),
    "/vendor/static".into(),
]);

// Resolve path
if let Some(path) = resolver.resolve("css/style.css") {
    println!("Found at: {}", path.display());
}

StaticFilesFinder

Finds static files across multiple directories.

use reinhardt::staticfiles::StaticFilesFinder;
use std::path::PathBuf;

let finder = StaticFilesFinder::new(vec![
    PathBuf::from("static"),
    PathBuf::from("assets"),
]);

// Find specific file
if let Ok(path) = finder.find("css/style.css") {
    println!("Found at: {}", path.display());
}

// Get all files
let all_files = finder.find_all();
// Returns: ["css/style.css", "js/app.js", "images/logo.png", ...]

Cache Strategies

CacheControlMiddleware

Controls caching for static files.

use reinhardt::staticfiles::{CacheControlMiddleware, CacheControlConfig};

let config = CacheControlConfig {
    public: true,
    max_age: 3600,        // 1 hour
    s_maxage: Some(86400), // 1 day on CDN
    immutable: true,       // Files never change
};

let middleware = CacheControlMiddleware::new(config);

Cache Directives

use reinhardt::staticfiles::CacheDirective;

// Typical static files (cache for 1 hour)
let directive = CacheDirective::public()
    .with_max_age(3600);

// Versioned files (cache for 1 year)
let immutable = CacheDirective::public()
    .with_max_age(31536000)
    .with_immutable(true);

// HTML files (don't cache)
let no_cache = CacheDirective::no_cache();

Router Integration

use reinhardt::ServerRouter;
use reinhardt::staticfiles::StaticFilesMiddleware;
use reinhardt::staticfiles::storage::StaticFilesConfig;

let router = ServerRouter::new()
    .with_middleware(StaticFilesMiddleware::new(
        StaticFilesConfig {
            static_root: "static".into(),
            static_url: "/static/".to_string(),
            ..Default::default()
        }
    ));

See Also