rust 46 lines · 6 steps

Response compression in Axum with tower-http

Attach a tower-http CompressionLayer to an Axum router so JSON responses are gzip/brotli/zstd compressed selectively.

Explained by highlit
1use axum::{
2 routing::get,
3 Json, Router,
4};
5use serde::Serialize;
6use tower_http::compression::{CompressionLayer, CompressionLevel};
7use tower_http::compression::predicate::{NotForContentType, SizeAbove};
8use tower_http::CompressionLevel::*;
9 
10#[derive(Serialize)]
11struct Article {
12 id: u64,
13 title: String,
14 body: String,
15}
16 
17async fn list_articles() -> Json<Vec<Article>> {
18 let articles = (1..=50)
19 .map(|id| Article {
20 id,
21 title: format!("Article #{id}"),
22 body: "Lorem ipsum dolor sit amet, ".repeat(64),
23 })
24 .collect();
25 
26 Json(articles)
27}
28 
29pub fn router() -> Router {
30 let predicate = SizeAbove::new(1024)
31 .and(NotForContentType::GRPC)
32 .and(NotForContentType::IMAGES)
33 .and(NotForContentType::const_new("application/wasm"));
34 
35 let compression = CompressionLayer::new()
36 .gzip(true)
37 .br(true)
38 .zstd(true)
39 .no_deflate()
40 .quality(CompressionLevel::Precise(6))
41 .compress_when(predicate);
42 
43 Router::new()
44 .route("/articles", get(list_articles))
45 .layer(compression)
46}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1A tower-http layer wraps every route so cross-cutting concerns like compression live outside handler logic.
  2. 2Compression predicates let you skip payloads that won't benefit — small bodies, already-compressed images, or gRPC.
  3. 3Enabling multiple encodings lets the layer negotiate the best one against each client's Accept-Encoding header.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Response compression in Axum with tower-http — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code