Files
card-to-iban/backend/internal/httpapi/client_ip.go

64 lines
1.3 KiB
Go

package httpapi
import (
"net"
"net/http"
"strings"
)
func extractClientIP(r *http.Request) *string {
if r == nil {
return nil
}
// Priority headers commonly set by reverse proxies, load balancers, and CDNs.
headers := []string{
"CF-Connecting-IP", // Cloudflare
"True-Client-IP", // Cloudflare Enterprise, Akamai
"X-Real-IP", // Nginx, Traefik
}
for _, header := range headers {
if val := strings.TrimSpace(r.Header.Get(header)); val != "" {
if ip := parseIP(val); ip != "" {
return &ip
}
}
}
// X-Forwarded-For can contain multiple comma-separated IPs: client, proxy1, proxy2. The original client IP is the first entry.
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
for _, part := range strings.Split(xff, ",") {
if ip := parseIP(part); ip != "" {
return &ip
}
}
}
// Fallback to RemoteAddr (typically "host:port" or "[ipv6]:port").
if r.RemoteAddr != "" {
host, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
host = r.RemoteAddr
}
if ip := parseIP(host); ip != "" {
return &ip
}
}
return nil
}
func parseIP(raw string) string {
raw = strings.TrimSpace(raw)
raw = strings.TrimPrefix(raw, "[")
raw = strings.TrimSuffix(raw, "]")
ip := net.ParseIP(raw)
if ip == nil {
return ""
}
return ip.String()
}