Hyper (Rust): Low-Level HTTP Server & Client Guide

Max WellsMax WellsFounder of Rustify

TL;DR: hyper is a low-level, high-performance HTTP/1.1 and HTTP/2 library for Rust. It handles the raw protocol; connection management, request/response parsing, keep-alive, and streaming. Most Rust developers don't use hyper directly; they use Axum (server) or Reqwest (client), which are built on top of hyper. Use hyper directly when you need fine-grained control over the HTTP stack, are building a framework, or have performance requirements that higher-level APIs can't meet.


What Is hyper?

hyper is a correct, fast, and production-battle-tested HTTP implementation in Rust. It is the HTTP engine that powers most of Rust's web ecosystem.

The dependency tree looks like this:

axum      ─┐
warp      ─┤─→  hyper  →  tokio  →  OS networking
reqwest   ─┘

hyper handles:

  • HTTP/1.1 and HTTP/2 protocol parsing
  • Connection pooling (client-side)
  • Keep-alive, pipelining, flow control
  • Streaming request and response bodies
  • TLS integration via rustls or native-tls

How Do You Use hyper as a Server?

As of hyper 1.x, you implement the Service trait (from Tower) or use hyper's lower-level connection API directly. Most users should use Axum instead.

[dependencies]
hyper = { version = "1", features = ["full"] }
hyper-util = { version = "0.1", features = ["tokio"] }
tokio = { version = "1", features = ["full"] }
http-body-util = "0.1"
use hyper::{body::Incoming, Request, Response};
use hyper::server::conn::http1;
use hyper::service::service_fn;
use http_body_util::Full;
use hyper::body::Bytes;
use tokio::net::TcpListener;
 
async fn handle(_req: Request<Incoming>) -> Result<Response<Full<Bytes>>, hyper::Error> {
    Ok(Response::new(Full::new(Bytes::from("Hello from hyper!"))))
}
 
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let listener = TcpListener::bind("127.0.0.1:3000").await?;
 
    loop {
        let (stream, _) = listener.accept().await?;
        tokio::spawn(async move {
            http1::Builder::new()
                .serve_connection(hyper_util::rt::TokioIo::new(stream), service_fn(handle))
                .await
                .unwrap();
        });
    }
}

This is significantly more verbose than Axum; which is the point. Use Axum unless you need this level of control.


How Do You Use hyper as an HTTP Client?

reqwest is the recommended HTTP client for most use cases; it wraps hyper with a high-level API. Use hyper directly when you need streaming, connection pooling control, or HTTP/2 push.

use hyper_util::client::legacy::Client;
use hyper_util::rt::TokioExecutor;
use http_body_util::Empty;
use hyper::body::Bytes;
use hyper::Request;
 
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client: Client<_, Empty<Bytes>> =
        Client::builder(TokioExecutor::new()).build_http();
 
    let uri = "http://httpbin.org/get".parse()?;
    let req = Request::get(uri).body(Empty::new())?;
    let res = client.request(req).await?;
    println!("Status: {}", res.status());
    Ok(())
}

How Does hyper Differ From Axum and Reqwest?

hyper is the engine. Axum and Reqwest are the cars; they add routing, extractors, middleware, JSON handling, and ergonomics on top.

hyperAxumReqwest
LevelLow-level protocolHigh-level serverHigh-level client
Routing❌ manual✅ built-inN/A
JSON / extractors❌ manual✅ built-in✅ built-in
MiddlewareTower ServiceTower layers
Use caseFramework authors, edge casesWeb APIs, appsHTTP client code
VerbosityHighLowLow

When Should You Use hyper Directly?

Building a custom HTTP framework, writing a reverse proxy, streaming large bodies with precise control, or benchmarking the absolute ceiling of Rust HTTP performance.

  • You're building a framework (Axum is built this way)
  • You need per-connection hooks not exposed by Axum
  • You're implementing an HTTP proxy or load balancer
  • You need HTTP/2 server push
  • You want to minimize dependencies (no routing overhead)

For everything else; APIs, web apps, microservices; use Axum.


Frequently Asked Questions

Yes; hyper powers Linkerd, Cloudflare services, AWS Lambda's runtime, and many other production systems. It's one of the most battle-tested Rust crates.

hyper 1.0 (released 2023) is a significant API redesign; more modular, removed built-in server utilities (moved to hyper-util), and updated to work with Tower's Service trait. Axum 0.7+ targets hyper 1.x.

hyper itself doesn't; it's TLS-agnostic. Use hyper-rustls (for rustls) or hyper-tls (for native-tls) to add TLS support. Reqwest handles this automatically.

hyper-util is a companion crate providing convenience utilities that were removed from hyper 1.x to keep the core minimal; including TokioIo, client connection pooling, and server utilities.


Sources


  • Axum: High-level web framework built on hyper
  • Reqwest: High-level HTTP client built on hyper
  • Tower: The Service trait abstraction hyper integrates with
  • Tokio: The async runtime hyper runs on
  • Async/Await: hyper is fully async; all operations return futures
  • Rustls: TLS stacks in modern Rust HTTP services are commonly built with rustls

Keep Reading

Ready to Land a $80-120k Rust Job?