realip

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 20, 2025 License: MIT Imports: 3 Imported by: 0

README

RealIP

Go Reference Go Report Card

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

WithHeaders

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

  1. Trusted peer check: If DisableTrustedPeerValidation = false, checks that the request came from a trusted IP in TrustedPeers. If not — returns the source IP.

  2. 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)
  3. IP parsing: The extracted value is parsed as an IP address

  4. 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

  • Go >=1.18

License

See LICENSE file

Documentation

Index

Constants

View Source
const (
	XRealIP       = "X-Real-IP"
	XForwardedFor = "X-Forwarded-For"
	TrueClientIP  = "True-Client-IP"
)

XRealIP, XForwardedFor and TrueClientIP are header keys used to extract the real client IP from the request. They represent common conventions for identifying the originating IP address of a client connecting through proxies or load balancers.

Variables

This section is empty.

Functions

func CtxWithRemoteIP

func CtxWithRemoteIP(ctx context.Context, peer netip.Addr, opts Options, getHeaderFn getHeaderFn) context.Context

func CtxWithRemoteIPOpts

func CtxWithRemoteIPOpts(
	ctx context.Context,
	peer netip.Addr,
	getHeaderFn getHeaderFn,
	opts ...Option,
) context.Context

func FromContext

func FromContext(ctx context.Context) (netip.Addr, bool)

FromContext extracts the real client IP from the context. It returns the IP and a boolean indicating if it was present.

func GetRemoteIP

func GetRemoteIP(peer netip.Addr, opts Options, getHeaderFn getHeaderFn) netip.Addr

func GetRemoteIPOpts

func GetRemoteIPOpts(peer netip.Addr, getHeaderFn getHeaderFn, opts ...Option) netip.Addr

Types

type Option

type Option func(*Options)

func WithDisableTrustedPeerValidation

func WithDisableTrustedPeerValidation(disable bool) Option

WithDisableTrustedPeerValidation disables validation of the client ip the request came from.

func WithHeaders

func WithHeaders(headers []string) Option

WithHeaders sets the headers to use in real IP extraction for requests from trusted peers.

func WithTrustedPeers

func WithTrustedPeers(peers []netip.Prefix) Option

WithTrustedPeers sets the trusted peers network prefixes.

func WithTrustedProxies

func WithTrustedProxies(proxies []netip.Prefix) Option

WithTrustedProxies sets the trusted proxies network prefixes.

func WithTrustedProxiesCount

func WithTrustedProxiesCount(count int) Option

WithTrustedProxiesCount sets the number of trusted proxies that may append X-Forwarded-For.

type Options

type Options struct {
	// TrustedPeers is a list of trusted peer network prefixes (CIDR).
	// If the client IP from which the request came is not on the list,
	// then the headers are considered to be spoofed and cannot be used to determine the IP address.
	// In this case, GetRemoteIP will return the client IP from which the request came.
	// Use DisableTrustedPeerValidation to disable this validation.
	TrustedPeers []netip.Prefix
	// DisableTrustedPeerValidation disables validation that the request came from a trusted client IP.
	// Useful then you are behind a trusted proxy and there are no direct requests from untrusted clients.
	// Please note that if you disable validation, headers may be spoofed if there are requests
	// that are not processed by a trusted proxy.
	DisableTrustedPeerValidation bool
	// TrustedProxies is a list of trusted proxies network prefixes (CIDR).
	// The first rightmost non-matching IP when going through X-Forwarded-For is considered the client IP.
	TrustedProxies []netip.Prefix
	// TrustedProxiesCount specifies the number of proxies in front that may append X-Forwarded-For.
	// It defaults to 0.
	TrustedProxiesCount int
	// Headers specifies the Headers to use in real IP extraction when the request is from a trusted peer.
	Headers []string
}

func EvaluateOpts

func EvaluateOpts(opts []Option) Options

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL