#reverse-proxy #axum #proxy #web-apps #web #http #http-request

axum-reverse-proxy

A flexible and efficient reverse proxy implementation for Axum web applications

7 releases (4 breaking)

new 0.5.1 Jan 4, 2025
0.4.1 Jan 4, 2025
0.3.1 Dec 26, 2024
0.2.1 Dec 25, 2024
0.1.0 Dec 25, 2024

#164 in HTTP server

Download history 329/week @ 2024-12-25 424/week @ 2025-01-01

753 downloads per month

MIT license

56KB
809 lines

axum-reverse-proxy

CI crates.io Documentation

A flexible and efficient reverse proxy implementation for Axum web applications. This library provides a simple way to forward HTTP requests from your Axum application to upstream servers. It is intended to be a simple implementation sitting on top of axum and hyper.

The eventual goal would be to benchmark ourselves against common reverse proxy libraries like nginx, traefik, haproxy, etc. We hope to achieve comparable (or better) performance but with significantly better developer ergonomics, using Rust code to configure the proxy instead of various configuration files with their own DSLs.

Features

  • 🛣️ Path-based routing
  • 🔄 Automatic retry mechanism with exponential backoff
  • 📨 Header forwarding (with host header management)
  • ⚙️ Configurable HTTP client settings
  • 🔌 Easy integration with Axum's Router
  • 🧰 Custom client configuration support
  • 🔒 HTTPS support
  • 📋 Optional RFC9110 compliance layer

Installation

Run cargo add axum-reverse-proxy to add the library to your project.

Quick Start

Here's a simple example that forwards all requests under /api to httpbin.org:

use axum::Router;
use axum_reverse_proxy::ReverseProxy;

// Create a reverse proxy that forwards requests from /api to httpbin.org
let proxy = ReverseProxy::new("/api", "https://httpbin.org");

// Convert the proxy to a router and use it in your Axum application
let app: Router = proxy.into();

Advanced Usage

Merging with Existing Router

You can merge the proxy with your existing Axum router:

use axum::{routing::get, Router, response::IntoResponse, extract::State};
use axum_reverse_proxy::ReverseProxy;

#[derive(Clone)]
struct AppState { foo: usize }

async fn root_handler(State(state): State<AppState>) -> impl IntoResponse {
    (axum::http::StatusCode::OK, format!("Hello, World! {}", state.foo))
}

let app: Router<AppState> = Router::new()
    .route("/", get(root_handler))
    .merge(ReverseProxy::new("/api", "https://httpbin.org"))
    .with_state(AppState { foo: 42 });

RFC9110 Compliance

The library includes an optional RFC9110 compliance layer that implements key requirements from RFC9110 (HTTP Semantics). To use it:

use axum_reverse_proxy::{ReverseProxy, Rfc9110Config, Rfc9110Layer};
use std::collections::HashSet;

// Create a config for RFC9110 compliance
let mut server_names = HashSet::new();
server_names.insert("example.com".to_string());

let config = Rfc9110Config {
    server_names: Some(server_names),  // For loop detection
    pseudonym: Some("myproxy".to_string()),  // For Via headers
    combine_via: true,  // Combine Via headers with same protocol
};

// Create a proxy with RFC9110 compliance
let proxy = ReverseProxy::new("/api", "https://api.example.com")
    .layer(Rfc9110Layer::with_config(config));

The RFC9110 layer provides:

  • Connection Header Processing: Properly handles Connection headers and removes hop-by-hop headers
  • Via Header Management: Adds and combines Via headers according to spec, with optional firewall mode
  • Max-Forwards Processing: Handles Max-Forwards header for TRACE/OPTIONS methods
  • Loop Detection: Detects request loops using Via headers and server names
  • End-to-end Header Preservation: Preserves end-to-end headers while removing hop-by-hop headers

Custom Client Configuration

For more control over the HTTP client behavior:

use axum_reverse_proxy::ReverseProxy;
use hyper_util::client::legacy::{Client, connect::HttpConnector};
use axum::body::Body;

let mut connector = HttpConnector::new();
connector.set_nodelay(true);
connector.enforce_http(false);
connector.set_keepalive(Some(std::time::Duration::from_secs(60)));

let client = Client::builder(hyper_util::rt::TokioExecutor::new())
    .pool_idle_timeout(std::time::Duration::from_secs(60))
    .pool_max_idle_per_host(32)
    .build(connector);

let proxy = ReverseProxy::new_with_client("/api", "https://api.example.com", client);

Configuration

The default configuration includes:

  • 3 retry attempts with exponential backoff
  • 60-second keepalive timeout
  • 10-second connect timeout
  • TCP nodelay enabled
  • Connection pooling with 32 idle connections per host
  • Automatic host header management

Examples

Check out the examples directory for more usage examples:

  • Basic Proxy - Shows how to set up a basic reverse proxy with path-based routing

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Dependencies

~12–22MB
~306K SLoC