rust 56 lines · 7 steps

Serving fingerprinted assets in Axum

An Axum router serves static files and picks Cache-Control headers based on whether each filename carries a content hash.

Explained by highlit
1use std::time::Duration;
2 
3use axum::{
4 http::{header, HeaderValue, Request},
5 Router,
6};
7use tower::ServiceBuilder;
8use tower_http::{
9 services::ServeDir,
10 set_header::SetResponseHeaderLayer,
11};
12 
13const IMMUTABLE_CACHE: &str = "public, max-age=31536000, immutable";
14 
15fn is_fingerprinted(name: &str) -> bool {
16 name.rsplit_once('.')
17 .and_then(|(stem, _ext)| stem.rsplit_once('.'))
18 .map(|(_, hash)| {
19 hash.len() >= 8 && hash.chars().all(|c| c.is_ascii_hexdigit())
20 })
21 .unwrap_or(false)
22}
23 
24pub fn assets_router() -> Router {
25 let serve_dir = ServeDir::new("dist/assets")
26 .precompressed_br()
27 .precompressed_gzip();
28 
29 let cache_control = SetResponseHeaderLayer::overriding(
30 header::CACHE_CONTROL,
31 |req: &Request<_>| {
32 let path = req.uri().path();
33 let name = path.rsplit('/').next().unwrap_or(path);
34 if is_fingerprinted(name) {
35 Some(HeaderValue::from_static(IMMUTABLE_CACHE))
36 } else {
37 Some(HeaderValue::from_static("public, max-age=3600"))
38 }
39 },
40 );
41 
42 let vary = SetResponseHeaderLayer::if_not_present(
43 header::VARY,
44 HeaderValue::from_static("Accept-Encoding"),
45 );
46 
47 Router::new().nest_service(
48 "/assets",
49 ServiceBuilder::new()
50 .layer(cache_control)
51 .layer(vary)
52 .service(serve_dir),
53 )
54}
55 
56fn _unused_hint(_d: Duration) {}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Fingerprinted filenames let you cache assets forever because a content change produces a new URL.
  2. 2Tower's layers let you attach response-header logic around a file-serving service declaratively.
  3. 3Deriving cache policy from the request path keeps caching correct without a manifest or config file.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Serving fingerprinted assets in Axum — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code