TL;DR:
rustlsis a modern TLS 1.2/1.3 implementation written in pure Rust; no OpenSSL dependency, no C code, memory-safe by construction. It is the recommended way to add HTTPS to Rust servers and clients. Useaxum-serverwithrustlsfor HTTPS in Axum,tokio-rustlsfor direct TLS on TCP streams, andreqwestwith therustls-tlsfeature for HTTPS client requests. Load certificates from files or usercgento generate self-signed certs for development.
What Is rustls?
rustls is a TLS library that implements the TLS 1.2 and 1.3 protocols in safe Rust, replacing the need for OpenSSL or native-tls in most Rust applications.
Why use rustls over OpenSSL:
- Memory safe: no C code → no buffer overflows, use-after-free
- No system dependency: statically linked; no
libsslrequired on deployment target - Modern defaults: TLS 1.3 by default, strong cipher suites only
- Audited: regularly security-audited by ISRG and others
How Do You Add HTTPS to an Axum Server?
[dependencies]
axum = "0.8"
axum-server = { version = "0.7", features = ["tls-rustls"] }
tokio = { version = "1", features = ["full"] }use axum::{Router, routing::get};
use axum_server::tls_rustls::RustlsConfig;
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/", get(|| async { "Hello, HTTPS!" }));
// Load TLS certificate and private key
let config = RustlsConfig::from_pem_file(
"certs/cert.pem",
"certs/key.pem",
)
.await
.unwrap();
let addr = std::net::SocketAddr::from(([0, 0, 0, 0], 443));
println!("Listening on https://{addr}");
axum_server::bind_rustls(addr, config)
.serve(app.into_make_service())
.await
.unwrap();
}How Do You Make HTTPS Requests With reqwest + rustls?
[dependencies]
reqwest = { version = "0.12", features = ["rustls-tls"], default-features = false }
tokio = { version = "1", features = ["full"] }use reqwest::Client;
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
// reqwest with rustls-tls uses rustls instead of native-tls
let client = Client::builder()
.use_rustls_tls()
.build()?;
let response = client
.get("https://api.example.com/data")
.header("Authorization", "Bearer token123")
.send()
.await?;
println!("Status: {}", response.status());
let body = response.text().await?;
println!("Body: {body}");
Ok(())
}How Do You Use tokio-rustls for Custom TLS Streams?
For direct TCP with TLS; lower level than axum-server.
[dependencies]
tokio-rustls = "0.26"
rustls = "0.23"use tokio_rustls::TlsAcceptor;
use rustls::ServerConfig;
use std::sync::Arc;
async fn create_tls_acceptor(cert_path: &str, key_path: &str) -> TlsAcceptor {
let certs = load_certs(cert_path);
let key = load_private_key(key_path);
let config = ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(certs, key)
.unwrap();
TlsAcceptor::from(Arc::new(config))
}
// Accept TLS connections
let acceptor = create_tls_acceptor("cert.pem", "key.pem").await;
let listener = tokio::net::TcpListener::bind("0.0.0.0:443").await.unwrap();
loop {
let (stream, _) = listener.accept().await.unwrap();
let tls_stream = acceptor.accept(stream).await.unwrap();
// handle tls_stream as a normal AsyncRead + AsyncWrite
}How Do You Generate Development Certificates?
[dev-dependencies]
rcgen = "0.13"use rcgen::generate_simple_self_signed;
fn generate_dev_certs() {
let subject_alt_names = vec!["localhost".to_string(), "127.0.0.1".to_string()];
let cert = generate_simple_self_signed(subject_alt_names).unwrap();
std::fs::write("certs/cert.pem", cert.cert.pem()).unwrap();
std::fs::write("certs/key.pem", cert.key_pair.serialize_pem()).unwrap();
}For production, use Let's Encrypt (via instant-acme crate) or load certificates issued by your CA.
Frequently Asked Questions
Prefer rustls for new projects; it is memory-safe, has no system dependencies, and is easier to deploy (no OpenSSL version mismatch). Use native-tls only when you need to trust the OS certificate store explicitly or have a legacy system requirement.
No; reqwest's default features include native-tls. Switch to rustls-tls by disabling default features:
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "json"] }No; rustls only supports TLS 1.2 and 1.3 by design. TLS 1.0 and 1.1 are deprecated and insecure. If you need legacy TLS support, use openssl directly (not recommended).
let client = reqwest::Client::builder()
.danger_accept_invalid_certs(true) // dev only!
.build()?;Never use this in production.
Sources
Related Glossary Terms
- Axum: axum-server uses rustls for HTTPS
- Tokio: tokio-rustls integrates TLS with async I/O
- Reqwest: HTTP client with rustls-tls feature
- Argon2: Rust security stacks often combine rustls for transport security with Argon2 for password hashing
- Tonic: Production gRPC services commonly pair Tonic with rustls for TLS
Keep Reading
- Rust Memory Safety: NSA and CISA: memory-safe TLS is a national security priority
- Rust at Cloudflare: Cloudflare uses Rustls for TLS termination at scale
- Rust in Windows Kernel: Microsoft adopting Rustls for secure communications
