Add Cloudflare header support for real client IP
- Updated IP geolocation to check CF-Connecting-IP header first - Added support for X-Real-IP header as additional fallback - Updated rate limiter to use same header priority for consistency - Headers checked in order: CF-Connecting-IP > X-Forwarded-For > X-Real-IP > remote_ip This ensures proper client IP detection when the site is behind Cloudflare. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
31bd5f5749
commit
fb34553fe9
2 changed files with 36 additions and 7 deletions
|
|
@ -68,12 +68,22 @@ defmodule AprsmeWeb.Plugs.IPGeolocation do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_client_ip(conn) do
|
defp get_client_ip(conn) do
|
||||||
# Check for forwarded IP first (when behind proxy/load balancer)
|
# Check for Cloudflare header first (CF-Connecting-IP)
|
||||||
|
cf_ip = get_req_header(conn, "cf-connecting-ip")
|
||||||
|
|
||||||
|
# Then check for standard forwarded headers
|
||||||
forwarded_for = get_req_header(conn, "x-forwarded-for")
|
forwarded_for = get_req_header(conn, "x-forwarded-for")
|
||||||
|
real_ip = get_req_header(conn, "x-real-ip")
|
||||||
|
|
||||||
ip =
|
ip =
|
||||||
case forwarded_for do
|
case {cf_ip, forwarded_for, real_ip} do
|
||||||
[forwarded | _] ->
|
{[cf | _], _, _} ->
|
||||||
|
# Cloudflare header takes precedence
|
||||||
|
ip_from_cf = String.trim(cf)
|
||||||
|
Logger.info("IP geolocation: Using Cloudflare CF-Connecting-IP: #{ip_from_cf}")
|
||||||
|
ip_from_cf
|
||||||
|
|
||||||
|
{[], [forwarded | _], _} ->
|
||||||
# Take the first IP from the X-Forwarded-For header
|
# Take the first IP from the X-Forwarded-For header
|
||||||
ip_from_header =
|
ip_from_header =
|
||||||
forwarded
|
forwarded
|
||||||
|
|
@ -84,7 +94,13 @@ defmodule AprsmeWeb.Plugs.IPGeolocation do
|
||||||
Logger.info("IP geolocation: Using forwarded IP from X-Forwarded-For header: #{ip_from_header}")
|
Logger.info("IP geolocation: Using forwarded IP from X-Forwarded-For header: #{ip_from_header}")
|
||||||
ip_from_header
|
ip_from_header
|
||||||
|
|
||||||
[] ->
|
{[], [], [real | _]} ->
|
||||||
|
# Use X-Real-IP header
|
||||||
|
ip_from_real = String.trim(real)
|
||||||
|
Logger.info("IP geolocation: Using X-Real-IP header: #{ip_from_real}")
|
||||||
|
ip_from_real
|
||||||
|
|
||||||
|
{[], [], []} ->
|
||||||
# Fall back to remote_ip
|
# Fall back to remote_ip
|
||||||
ip_from_remote =
|
ip_from_remote =
|
||||||
case conn.remote_ip do
|
case conn.remote_ip do
|
||||||
|
|
|
||||||
|
|
@ -40,9 +40,22 @@ defmodule AprsmeWeb.Plugs.RateLimiter do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_key(conn, :ip) do
|
defp get_key(conn, :ip) do
|
||||||
case get_req_header(conn, "x-forwarded-for") do
|
# Check headers in order of preference
|
||||||
[ip | _] -> ip
|
cond do
|
||||||
[] -> conn.remote_ip |> :inet.ntoa() |> to_string()
|
# Cloudflare header takes precedence
|
||||||
|
cf_ip = get_req_header(conn, "cf-connecting-ip") != [] ->
|
||||||
|
hd(cf_ip)
|
||||||
|
|
||||||
|
# Then standard forwarded headers
|
||||||
|
forwarded = get_req_header(conn, "x-forwarded-for") != [] ->
|
||||||
|
forwarded |> hd() |> String.split(",") |> List.first() |> String.trim()
|
||||||
|
|
||||||
|
real_ip = get_req_header(conn, "x-real-ip") != [] ->
|
||||||
|
hd(real_ip)
|
||||||
|
|
||||||
|
# Fall back to remote_ip
|
||||||
|
true ->
|
||||||
|
conn.remote_ip |> :inet.ntoa() |> to_string()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue