๐ฉ๏ธ Cloudflare vs. My Blog Views: The "Real" IP Address Adventure! ๐ต๏ธโโ๏ธ
๐ฉ๏ธ Cloudflare vs. My Blog Views: The "Real" IP Address Adventure! ๐ต๏ธโโ๏ธ
So, you've got your awesome Rust and Axum blog chugging along, serving up brilliant content. You've even been smart and put Cloudflare in for that caching and security. But then you peek at your post view counts, and they look a bit... sad. ๐ What gives?!
If you're tracking views by IP address (a common way to get somewhat unique views), Cloudflare can throw a wrench in the works. Instead of seeing your readers' actual IP addresses, your server sees Cloudflare's IP addresses. So, 1.2.3.4
reads a post, then 5.6.7.8
reads it, but if they both came through the same Cloudflare edge node, your server might think, "Hey, I've seen this IP before!" and not count the second view.
Bummer...
The Culprit: Who's Knocking? ๐ช
In Axum, you might be grabbing the IP like this:
// The "before" - getting the direct connection IP
use axum::extract::ConnectInfo;
use std::net::SocketAddr;
async fn show_post_detail(
// ... other extractors ...
ConnectInfo(addr): ConnectInfo<SocketAddr>,
) {
let ip_address_string = addr.ip().to_string();
// ... go count the view using ip_address_string ...
}
When Cloudflare is involved, addr.ip()
often returns a Cloudflare IP.
The Fix: "Hey Cloudflare, Who's Really There?" ๐ฃ๏ธ
Luckily, Cloudflare tells you the original visitor's IP address through a special HTTP header, usually CF-Connecting-IP
. We need to tell Axum to look for it!
Here's how we updated our handler:
// The "after" - checking CF-Connecting-IP
use axum::{
extract::{ConnectInfo, State, Path /*...other needed extracts...*/},
http::HeaderMap, // <-- Needed to get headers!
// ...
};
use std::net::SocketAddr;
// ... other imports ...
async fn show_post_detail(
// ... other extractors ...
ConnectInfo(addr): ConnectInfo<SocketAddr>,
headers: HeaderMap, // <-- Magic! โจ
// ...
) {
let client_ip_from_header = headers
.get("CF-Connecting-IP") // The golden ticket header
.and_then(|value| value.to_str().ok())
.map(String::from);
let ip_address_string = client_ip_from_header
.unwrap_or_else(|| addr.ip().to_string()); // Fallback just in case
// ... now count the view with the *real* IP! ๐ ...
}
The Result?
More accurate view counts! ๐ Now our server knows who's actually reading our stuff, even when they're peeking from behind Cloudflare's curtain.
Important Note: When using headers like CF-Connecting-IP
, make sure your server is configured to only accept traffic from Cloudflare IPs. Otherwise, a sneaky visitor could send that header themselves and trick your app! Most setups behind Cloudflare handle this, but it's good to be aware.
Happy coding, and may your view counts be ever (not so accurately) climbing! ๐