RealIP

A Go package for extracting the real client IP address from HTTP requests in applications behind proxy servers, load balancers, or CDNs.
Features
- 🔒 Security: Trusted proxy validation to prevent IP spoofing
- 🎯 Flexibility: Support for multiple headers (
X-Forwarded-For, X-Real-IP, True-Client-IP)
- ⚡ Performance: Uses
netip.Addr from the Go standard library
- 🔧 Configurable: Functional options for flexible configuration
- 📦 Minimalist: No external dependencies
Installation
go get github.com/SnapSuzun/go-realip
Quick Start
Basic Usage
package main
import (
"context"
"net/netip"
"github.com/SnapSuzun/go-realip"
)
func main() {
// Client IP address from which the request came
peerIP := netip.MustParseAddr("10.0.0.1")
// Function to get header value
getHeader := func(name string) string {
// Your header retrieval logic
if name == realip.XRealIP {
return "203.0.113.45"
}
return ""
}
// Get real IP
ip := realip.GetRemoteIPOpts(
peerIP,
getHeader,
realip.WithHeaders([]string{realip.XRealIP, realip.XForwardedFor}),
realip.WithDisableTrustedPeerValidation(true),
)
println(ip.String()) // 203.0.113.45
}
Using with Context
func handler(ctx context.Context, peerIP netip.Addr, getHeader func(string) string) {
// Add IP to context
ctx = realip.CtxWithRemoteIPOpts(
ctx,
peerIP,
getHeader,
realip.WithHeaders([]string{realip.XRealIP}),
realip.WithDisableTrustedPeerValidation(true),
)
// Extract IP from context
if ip, ok := realip.FromContext(ctx); ok {
println("Client IP:", ip.String())
}
}
HTTP Server Integration
package main
import (
"context"
"net/http"
"net/netip"
"github.com/SnapSuzun/go-realip"
)
func realIPMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Get client IP
peerIP, _ := netip.ParseAddrPort(r.RemoteAddr)
// Function to get headers
getHeader := func(name string) string {
return r.Header.Get(name)
}
// Add real IP to context
ctx := realip.CtxWithRemoteIPOpts(
r.Context(),
peerIP.Addr(),
getHeader,
realip.WithHeaders([]string{
realip.XRealIP,
realip.XForwardedFor,
realip.TrueClientIP,
}),
realip.WithTrustedPeers([]netip.Prefix{
netip.MustParsePrefix("10.0.0.0/8"),
netip.MustParsePrefix("172.16.0.0/12"),
}),
realip.WithTrustedProxies([]netip.Prefix{
netip.MustParsePrefix("10.0.0.0/8"),
}),
)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func mainHandler(w http.ResponseWriter, r *http.Request) {
if ip, ok := realip.FromContext(r.Context()); ok {
w.Write([]byte("Your IP: " + ip.String()))
} else {
w.Write([]byte("IP not found"))
}
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", mainHandler)
http.ListenAndServe(":8080", realIPMiddleware(mux))
}
Configuration Options
Sets the list of headers to extract IP addresses from. They are checked in the specified order.
realip.WithHeaders([]string{
realip.XRealIP, // "X-Real-IP"
realip.XForwardedFor, // "X-Forwarded-For"
realip.TrueClientIP, // "True-Client-IP"
})
Order matters: the first valid IP found will be used.
WithTrustedPeers
Defines trusted networks (CIDR) from which requests may come. If a request came from a non-trusted peer, headers are ignored and the source IP is returned.
realip.WithTrustedPeers([]netip.Prefix{
netip.MustParsePrefix("10.0.0.0/8"), // Private networks
netip.MustParsePrefix("172.16.0.0/12"),
netip.MustParsePrefix("192.168.0.0/16"),
netip.MustParsePrefix("203.0.113.0/24"), // Your load balancer
})
WithDisableTrustedPeerValidation
Disables trusted peer validation. Use only if you are sure that all requests go through a trusted proxy.
realip.WithDisableTrustedPeerValidation(true)
⚠️ Warning: When validation is disabled, headers can be spoofed by attackers on direct requests.
WithTrustedProxies
Defines trusted proxy servers for processing X-Forwarded-For. The package will find the first IP from right to left that is not in the trusted proxy list.
realip.WithTrustedProxies([]netip.Prefix{
netip.MustParsePrefix("10.0.0.0/8"),
})
Example: With X-Forwarded-For: 203.0.113.1, 10.0.0.5, 10.0.0.6 will return 203.0.113.1.
WithTrustedProxiesCount
Specifies the number of trusted proxy servers in front of the application that may append to X-Forwarded-For.
realip.WithTrustedProxiesCount(2) // 2 proxies in front of the application
Usage Examples
Example 1: Behind Nginx Proxy
opts := []realip.Option{
realip.WithHeaders([]string{realip.XRealIP}),
realip.WithTrustedPeers([]netip.Prefix{
netip.MustParsePrefix("10.0.1.0/24"), // Nginx network
}),
}
ip := realip.GetRemoteIPOpts(peerIP, getHeader, opts...)
Example 2: Behind Cloudflare
opts := []realip.Option{
realip.WithHeaders([]string{realip.TrueClientIP, realip.XForwardedFor}),
realip.WithTrustedPeers([]netip.Prefix{
// Cloudflare IP ranges
netip.MustParsePrefix("173.245.48.0/20"),
netip.MustParsePrefix("103.21.244.0/22"),
// ... other Cloudflare ranges
}),
}
ip := realip.GetRemoteIPOpts(peerIP, getHeader, opts...)
Example 3: Multiple Proxy Layers
// Client -> Cloudflare -> AWS ALB -> Your application
opts := []realip.Option{
realip.WithHeaders([]string{realip.XForwardedFor}),
realip.WithTrustedPeers([]netip.Prefix{
netip.MustParsePrefix("10.0.0.0/8"), // AWS VPC
}),
realip.WithTrustedProxies([]netip.Prefix{
netip.MustParsePrefix("10.0.0.0/8"), // AWS internal
}),
realip.WithTrustedProxiesCount(2), // Cloudflare + ALB
}
ip := realip.GetRemoteIPOpts(peerIP, getHeader, opts...)
Example 4: Using Options Struct
options := realip.Options{
TrustedPeers: []netip.Prefix{
netip.MustParsePrefix("10.0.0.0/8"),
},
DisableTrustedPeerValidation: false,
TrustedProxies: []netip.Prefix{
netip.MustParsePrefix("10.0.0.0/8"),
},
TrustedProxiesCount: 1,
Headers: []string{
realip.XRealIP,
realip.XForwardedFor,
},
}
ip := realip.GetRemoteIP(peerIP, options, getHeader)
How It Works
-
Trusted peer check: If DisableTrustedPeerValidation = false, checks that the request came from a trusted IP in TrustedPeers. If not — returns the source IP.
-
Header processing: Headers from the Headers list are checked in order:
- For
X-Forwarded-For, special logic is used considering TrustedProxies and TrustedProxiesCount
- For other headers, the last value is taken (right to left through commas)
-
IP parsing: The extracted value is parsed as an IP address
-
Fallback: If no valid IP is found in headers, the source request IP is returned
Security
Important Points
- ✅ Always use
WithTrustedPeers if there is a possibility of direct requests to your application
- ✅ Minimize the list of trusted networks — specify only real proxy/load balancer IPs
- ⚠️ Do not use
DisableTrustedPeerValidation if the application may receive direct requests
- ✅ Regularly update trusted IP lists (e.g., Cloudflare ranges change)
Example of Unsafe Configuration
// ❌ UNSAFE: validation disabled, headers can be spoofed
opts := []realip.Option{
realip.WithHeaders([]string{realip.XForwardedFor}),
realip.WithDisableTrustedPeerValidation(true),
}
Example of Safe Configuration
// ✅ SAFE: only requests from trusted proxies are checked
opts := []realip.Option{
realip.WithHeaders([]string{realip.XForwardedFor, realip.XRealIP}),
realip.WithTrustedPeers([]netip.Prefix{
netip.MustParsePrefix("10.0.1.0/24"), // Exact range of your proxy
}),
}
API Reference
Constants
const (
XRealIP = "X-Real-IP"
XForwardedFor = "X-Forwarded-For"
TrueClientIP = "True-Client-IP"
)
Functions
GetRemoteIPOpts
func GetRemoteIPOpts(peer netip.Addr, getHeaderFn func(string) string, opts ...Option) netip.Addr
Extracts the real client IP address using functional options.
GetRemoteIP
func GetRemoteIP(peer netip.Addr, opts Options, getHeaderFn func(string) string) netip.Addr
Extracts the real client IP address using the Options struct.
CtxWithRemoteIPOpts
func CtxWithRemoteIPOpts(ctx context.Context, peer netip.Addr, getHeaderFn func(string) string, opts ...Option) context.Context
Adds the real IP address to the context.
CtxWithRemoteIP
func CtxWithRemoteIP(ctx context.Context, peer netip.Addr, opts Options, getHeaderFn func(string) string) context.Context
Adds the real IP address to the context using the Options struct.
FromContext
func FromContext(ctx context.Context) (netip.Addr, bool)
Extracts the IP address from the context. Returns the IP and a success flag.
Requirements
License
See LICENSE file