Axum Versus Actix Web and How to Choose With Numbers
Axum and Actix Web are both fast and mature. The real differences are the runtime model, middleware ecosystem and ergonomics, and this post lays out how to decide.

Choosing a framework on a benchmark chart is how teams end up with the fast framework that nobody likes writing. Axum and Actix Web are both well past the point where throughput decides anything for a typical service, so the useful differences are structural. This post lays them out and ends with a recommendation. It is a decision piece in the Axum production service cluster.
Axum 0.8, Actix Web 4, Rust 1.90.
The same handler in both
Axum:
use axum::{extract::Path, routing::get, Json, Router};
async fn hello(Path(name): Path<String>) -> Json<String> {
Json(format!("hello {name}"))
}
fn app() -> Router {
Router::new().route("/hello/{name}", get(hello))
}Actix Web:
use actix_web::{get, web, App, HttpServer, Responder};
#[get("/hello/{name}")]
async fn hello(name: web::Path<String>) -> impl Responder {
web::Json(format!("hello {}", name.into_inner()))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| App::new().service(hello))
.bind(("127.0.0.1", 8080))?
.run()
.await
}The logic is the same. The differences are around it.
What actually differs
| Axis | Axum | Actix Web |
|---|---|---|
| Foundation | Hyper and Tower | Own service layer on Tokio |
| Middleware | Tower Layer, from_fn | Own Transform trait, wrap_fn |
| Handlers | Send futures, work stealing runtime | Per worker runtime, handlers need not be Send |
| State | State<T> with Clone | web::Data<T> shared via Arc |
| Routing macros | Builder methods | Builder or attribute macros |
| Maintenance | Tokio project | Actix community |
The runtime model is the one that changes how code looks. In Actix each worker thread has its own runtime and its own copy of the app factory closure, so state shared across workers needs an explicit Arc. In Axum one multi thread runtime serves all requests, so every handler future is Send; the consequences are in Send and Sync.
The benchmark question
Actix Web results in public plaintext and JSON benchmarks are often at the top, and Axum is close behind. These tests measure the framework doing almost nothing. A handler that makes one PostgreSQL query spends orders of magnitude longer in the driver. We do not report a number for this post, because the useful figure is yours: load test your real endpoint, with a tool such as oha or wrk, and compare p99 latency.
Ecosystem fit
If you use tower-http for compression, CORS and tracing, or tonic for gRPC, Axum shares the same types, and layers compose across them. Actix has its own equivalents for most things, and some require the actix- prefixed crate. For authentication and sessions, the Axum side leans on extractors and axum-extra; the Actix side has actix-session and actix-identity.
So which one
Choose Axum when the project is new, the team is mixed, or you value the Tower ecosystem. Choose Actix Web when the team already has production experience with it, or when you need its specific features. Do not migrate a working Actix service for performance.
When this advice flips
Edge cases: if you must run handlers that are not Send (some FFI handles), Actix's model is friendlier; if you want a single binary serving gRPC and HTTP on one port, Axum with tonic is easier. Each project documents its design in its own repository: axum on GitHub and actix-web on GitHub.


