axum-client-ip
Client IP address extractors for Axum
There are two distinct use cases for client IP which should be treated differently:
SecureClientIp
] or an extractor for a
particular header.InsecureClientIp
].For a deep dive into the trade-off refer to this Adam Pritchard's article
SecureClientIp
vs specific header extractorsApart 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.
```rust,norun use axum::{routing::get, Router}; use axumclient_ip::{InsecureClientIp, SecureClientIp, SecureClientIpSource}; use std::net::SocketAddr;
async fn handler(insecureip: InsecureClientIp, secureip: SecureClientIp) -> String { format!("{insecureip:?} {secureip:?}") }
async fn main() { async fn handler(insecureip: InsecureClientIp, secureip: SecureClientIp) -> String { format!("{insecureip:?} {secureip:?}") }
let app = Router::new().route("/", get(handler))
.layer(SecureClientIpSource::ConnectInfo.into_extension());
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(
// Don't forget to add `ConnectInfo` if you aren't behind a proxy
app.into_make_service_with_connect_info::<SocketAddr>()
)
.await
.unwrap()
} ```
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!
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:
bash
cargo sync-readme
(make sure the cargo command is installed):
bash
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.