10 releases (5 breaking)

new 0.6.0 Apr 9, 2024
0.5.1 Mar 2, 2024
0.5.0 Nov 27, 2023
0.4.1 Apr 26, 2023
0.1.0 Feb 9, 2022

#221 in Network programming

Download history 4612/week @ 2023-12-22 5246/week @ 2023-12-29 5256/week @ 2024-01-05 4193/week @ 2024-01-12 5405/week @ 2024-01-19 4177/week @ 2024-01-26 3595/week @ 2024-02-02 4199/week @ 2024-02-09 3927/week @ 2024-02-16 3217/week @ 2024-02-23 3143/week @ 2024-03-01 3220/week @ 2024-03-08 3401/week @ 2024-03-15 6365/week @ 2024-03-22 6400/week @ 2024-03-29 3787/week @ 2024-04-05

20,564 downloads per month
Used in 6 crates

MIT license

739 lines

License Crates.io Docs.rs


Client IP address extractors for Axum

Why different extractors?

There are two distinct use cases for client IP which should be treated differently:

  1. You can't tolerate the possibility of spoofing (you're working on rate limiting, spam protection, etc). In this case, you should use SecureClientIp or an extractor for a particular header.
  2. You can trade potential spoofing for a statistically better IP determination. E.g. you use the IP for geolocation when the correctness of the location isn't critical for your app. For something like this, you can use InsecureClientIp.

For a deep dive into the trade-off refer to this Adam Pritchard's article

SecureClientIp vs specific header extractors

Apart from SecureClientIp there are Forwarded, RightmostForwarded, XForwardedFor, RightmostXForwardedFor, FlyClientIp, TrueClientIp, CfConnectingIp and XRealIp extractors.

They work the same way - by extracting IP from the specified header you control. The only difference is in the target header specification. With SecureClientIp you can specify the header at runtime, so you can use e.g. environment variable for this setting (look at the implementation example). While with specific extractors you'd need to recompile your code if you'd like to change the target header (e.g. you're moving to another cloud provider). To mitigate this change you can create a type alias e.g. type InsecureIp = XRealIp and use it in your handlers, then the change will affect only one line.


use axum::{routing::get, Router};
use axum_client_ip::{InsecureClientIp, SecureClientIp, SecureClientIpSource};
use std::net::SocketAddr;

async fn handler(insecure_ip: InsecureClientIp, secure_ip: SecureClientIp) -> String {
    format!("{insecure_ip:?} {secure_ip:?}")

async fn main() {
    async fn handler(insecure_ip: InsecureClientIp, secure_ip: SecureClientIp) -> String {
        format!("{insecure_ip:?} {secure_ip:?}")

    let app = Router::new().route("/", get(handler))

    let addr = SocketAddr::from(([0, 0, 0, 0], 3000));
    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
        // Don't forget to add `ConnectInfo` if you aren't behind a proxy

A common issue with Axum extractors

The most often issue with this extractor is using it after one consuming body e.g. axum::extract::Json. To fix this rearrange extractors in your handler definition moving body consumption to the end, see details.


We appreciate all kinds of contributions, thank you!

Note on README

Most of the readme is automatically copied from the crate documentation by cargo-sync-readme. This way the readme is always in sync with the docs and examples are tested.

So if you find a part of the readme you'd like to change between <!-- cargo-sync-readme start --> and <!-- cargo-sync-readme end --> markers, don't edit README.md directly, but rather change the documentation on top of src/lib.rs and then synchronize the readme with:

cargo sync-readme

(make sure the cargo command is installed):

cargo install cargo-sync-readme

If you have rusty-hook installed the changes will apply automatically on commit.


This project is licensed under the MIT license.


~205K SLoC