rust 46 lines · 8 steps

How a custom ClientIp extractor works in Axum

A custom Axum extractor resolves a client's IP from proxy headers, falling back to the raw socket address.

Explained by highlit
1use std::net::{IpAddr, SocketAddr};
2 
3use axum::extract::{ConnectInfo, FromRequestParts};
4use axum::http::request::Parts;
5use axum::http::{HeaderMap, StatusCode};
6 
7pub struct ClientIp(pub IpAddr);
8 
9impl<S> FromRequestParts<S> for ClientIp
10where
11 S: Send + Sync,
12{
13 type Rejection = (StatusCode, &'static str);
14 
15 async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
16 if let Some(ip) = ip_from_forwarded(&parts.headers) {
17 return Ok(ClientIp(ip));
18 }
19 
20 if let Some(ip) = parts.headers.get("x-real-ip").and_then(parse_header) {
21 return Ok(ClientIp(ip));
22 }
23 
24 let ConnectInfo(addr) = parts
25 .extensions
26 .get::<ConnectInfo<SocketAddr>>()
27 .copied()
28 .ok_or((StatusCode::INTERNAL_SERVER_ERROR, "missing connection info"))?;
29 
30 Ok(ClientIp(addr.ip()))
31 }
32}
33 
34fn ip_from_forwarded(headers: &HeaderMap) -> Option<IpAddr> {
35 headers
36 .get("x-forwarded-for")?
37 .to_str()
38 .ok()?
39 .split(',')
40 .map(str::trim)
41 .find_map(|candidate| candidate.parse::<IpAddr>().ok())
42}
43 
44fn parse_header(value: &axum::http::HeaderValue) -> Option<IpAddr> {
45 value.to_str().ok()?.trim().parse().ok()
46}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Implementing FromRequestParts lets you turn any request-derived value into a first-class handler argument.
  2. 2Resolving a client IP behind proxies means trusting forwarding headers first and treating the socket address as the last resort.
  3. 3Returning a typed Rejection keeps error handling explicit when required request context is missing.

Related explainers

Share this explainer

Here's the card — post it anywhere.

How a custom ClientIp extractor works in Axum — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code