A Rust library for compressing and embedding static assets in a web server using Axum. This crate provides efficient asset embedding with optional compression (gzip and zstd) and conditional requests support.
-
Embed static assets at compile-time for efficient serving
-
Automatic compression with
gzipandzstd -
ETag support for conditional requests and caching
-
HTTP Range Requests for partial content responses (RFC 7233)
-
Seamless Axum integration with request extraction for encoding and caching headers
Add the following to your Cargo.toml:
[dependencies]
static-serve = "0.4"
axum = "0.8"Use the embed_assets! macro to create a static_router() function in scope which will include your static files, embedding them into your binary:
use static_serve::embed_assets;
embed_assets!("assets", compress = true, ignore_paths = ["temp.txt","temp"], cache_busted_paths = ["immutable"]);
let router = static_router();This will:
- Include all files from the
assetsdirectory excepttemp.txtand thetempdirectory - Compress them using
gzipandzstd(if beneficial) - For only files in
assets/immutable, add aCache-Controlheader withpublic, max-age=31536000, immutable(since these are marked as cache-busted paths) - Generate a
static_router()function to serve these assets
path_to_dir- a valid&strstring literal of the path to the static files to be included
-
compress = false- compress static files with zstd and gzip, true or false (defaults to false) -
ignore_paths = ["my_ignore_dir", "other_ignore_dir", "my_ignore_file.txt"]- a bracketed list of&strs of paths/subdirectories/files inside the target directory, which should be ignored and not included. (If this parameter is missing, no paths/subdirectories/files will be ignored) -
strip_html_ext = false- strips the.htmlor.htmfrom all HTML files included. If the filename isindex.htmlorindex.htm, theindexpart will also be removed, leaving just the root (defaults to false) -
cache_busted_paths = ["my_immutables_dir", "my_immutable_file"]- a bracketed list of&strs of the subdirectories and/or single files which should gain theCache-Controlheader withpublic, max-age=31536000, immutablefor cache-busted paths. If this parameter is missing, the default is that no embedded files will have theCache-Controlheader. Note: the files incache_busted_pathsneed to already be compatible with cache-busting by having hashes in their file paths (for example). Allstatic-servedoes is set the appropriate header. -
allow_unknown_extensions = false- serve files with unknown extensions asapplication/octet-streamcontent-type; when not set totrue, compilation fails if a content type cannot be guessed from the extension, or if the file has no extension
Use the embed_asset! macro to return a function you can use as a GET handler, which will include your static file, embedded into your binary:
use axum::Router;
use static_serve::embed_asset;
let router: Router<()> = Router::new();
let handler = embed_asset!("assets/my_file.png", compress = true, cache_bust = true);
let router = router.route("/my_file.png", handler);This will:
- Include the file
my_file.pngfrom theassetsdirectory - Compress it using
gzipandzstd(if beneficial) - Add a
Cache-Controlheader with the valuepublic, max-age=31536000, immutablefor cache-busted paths. Note: the file inembed_asset!needs to already be compatible with cache-busting by having a hash in its file path (for example). Allstatic-servedoes is set the appropriate header. - Generate a
MethodRouter"handler" you can add as a route on your router to serve the file
path_to_file- a valid&strstring literal of the path to the static file to be included
compress = false- compress a static file with zstd and gzip, true or false (defaults to false)cache_bust = false- add aCache-Controlheader with the valuepublic, max-age=31536000, immutablefor a cache-busted asset (defaults to false)allow_unknown_extensions = false- serve files with unknown extensions asapplication/octet-streamcontent-type; when not set totrue, compilation fails if a content type cannot be guessed from the extension, or if the file has no extension
The crate automatically handles:
-
Accept-Encodingheader to serve compressed versions if available -
If-None-Matchheader for ETag validation, returning304 Not Modifiedif unchanged -
With the optional cache-bust headers feature, each embedded file in the
cache_busted_pathsarray (or single file in the case ofembed_asset!withcache_bust = true) will be returned with aCache-Controlheader with the valuepublic, max-age=31536000, immutable. Note: the files involved need to already be compatible with cache-busting by having hashes in their file paths (for example). Allstatic-servedoes is set the appropriate header. -
Accept-Ranges: bytesis advertised on all successful responses. When aRangeheader is present, the server responds with206 Partial Contentand the requested byte range, or416 Range Not Satisfiableif the range is invalid. Compression is automatically disabled for range requests since byte offsets refer to the uncompressed body.
use axum::{Router, Server};
use static_serve::{embed_assets, embed_asset};
embed_assets!("public", compress = true);
#[tokio::main]
async fn main() {
let router = static_router();
let my_file_handler = embed_asset!("other_files/my_file.txt");
let router = router.route("/other_files/my_file.txt", my_file_handler);
Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(router.into_make_service())
.await
.unwrap();
}Licensed under either of
- Apache License, Version 2.0, (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.