Documentation
¶
Overview ¶
Package clientip provides secure client IP extraction from HTTP requests and framework-agnostic request inputs with support for proxy chains, trusted proxy validation, and multiple header sources.
Features ¶
- Security-first design with protection against IP spoofing and header injection
- Flexible proxy configuration with min/max trusted proxy ranges in proxy chains
- Multiple source support: Forwarded, X-Forwarded-For, X-Real-IP, RemoteAddr, custom headers
- Framework-friendly RequestInput API for non-net/http integrations
- Safe defaults: RemoteAddr-only unless header sources are explicitly configured
- Deployment presets for common topologies (direct, loopback proxy, VM proxy)
- Optional observability with context-aware logging and pluggable metrics
- Type-safe using modern Go netip.Addr
Basic Usage ¶
Simple extraction without proxy configuration:
extractor, err := clientip.New()
if err != nil {
log.Fatal(err)
}
extraction, err := extractor.Extract(req)
if err != nil {
log.Printf("extract failed: %v", err)
return
}
fmt.Printf("Client IP: %s from %s\n", extraction.IP, extraction.Source)
Framework-agnostic input is available via ExtractFrom:
extraction, err := extractor.ExtractFrom(clientip.RequestInput{
Context: ctx,
RemoteAddr: remoteAddr,
Path: path,
Headers: headerProvider,
})
Behind Reverse Proxy ¶
Configure trusted proxy prefixes with flexible min/max proxy count:
cidrs, _ := clientip.ParseCIDRs("10.0.0.0/8", "172.16.0.0/12")
extractor, err := clientip.New(
clientip.TrustProxyPrefixes(cidrs...), // Trust upstream proxy ranges
clientip.MinTrustedProxies(0), // Count trusted proxies present in proxy headers
clientip.MaxTrustedProxies(2),
clientip.Priority(clientip.SourceXForwardedFor, clientip.SourceRemoteAddr),
clientip.WithChainSelection(clientip.RightmostUntrustedIP),
clientip.AllowPrivateIPs(false),
)
Custom Headers ¶
Support for cloud providers and custom proxy headers:
extractor, _ := clientip.New(
clientip.TrustLoopbackProxy(),
clientip.Priority(
"CF-Connecting-IP", // Cloudflare
clientip.SourceXForwardedFor,
clientip.SourceRemoteAddr,
),
)
Header sources require trusted upstream proxy ranges. Use TrustProxyPrefixes (with ParseCIDRs for string inputs) or helper options like TrustLoopbackProxy, TrustPrivateProxyRanges, TrustLocalProxyDefaults, or TrustProxyAddrs.
Presets are available for common setups:
extractor, _ := clientip.New(clientip.PresetVMReverseProxy())
Observability ¶
Add logging and metrics for production monitoring: (Prometheus adapter package: github.com/abczzz13/clientip/prometheus) The logger receives req.Context(), allowing trace/span IDs to flow through.
import clientipprom "github.com/abczzz13/clientip/prometheus"
metrics, _ := clientipprom.New()
extractor, err := clientip.New(
clientip.TrustProxyPrefixes(cidrs...),
clientip.MinTrustedProxies(0),
clientip.MaxTrustedProxies(3),
clientip.Priority(clientip.SourceXForwardedFor, clientip.SourceRemoteAddr),
clientip.WithLogger(slog.Default()),
clientip.WithMetrics(metrics),
)
Security Considerations ¶
The package includes several security features:
- Detection of malformed Forwarded headers and duplicate single-IP header values
- Immediate proxy trust enforcement before honoring Forwarded/X-Forwarded-For
- Validation of proxy counts (min/max enforcement)
- Chain length limits to prevent DoS
- Rejection of invalid/implausible IPs (loopback, multicast, etc.)
- Optional private IP filtering and explicit reserved CIDR allowlisting
- Strict fail-closed behavior by default (SecurityModeStrict)
Security Anti-Patterns ¶
- Do not combine multiple competing header sources for security decisions.
- Do not use SecurityModeLax for ACL/risk/authz enforcement paths.
- Do not trust broad proxy CIDRs unless they are truly controlled by your edge.
Security Modes ¶
Security behavior can be configured per extractor:
- SecurityModeStrict (default): fail closed on security-significant errors and invalid present source values.
- SecurityModeLax: allow fallback to lower-priority sources for those errors.
Example:
extractor, _ := clientip.New(
clientip.WithSecurityMode(clientip.SecurityModeLax),
)
Thread Safety ¶
Extractor instances are safe for concurrent use. They are typically created once at application startup and reused across all requests.
Index ¶
- Constants
- Variables
- func ExtractAddrFromWithOptions(input RequestInput, opts ...Option) (netip.Addr, error)
- func ExtractAddrWithOptions(r *http.Request, opts ...Option) (netip.Addr, error)
- func NormalizeSourceName(headerName string) string
- func ParseCIDRs(cidrs ...string) ([]netip.Prefix, error)
- type ChainDebugInfo
- type ChainSelection
- type ChainTooLongError
- type Extraction
- type ExtractionError
- type Extractor
- func (e *Extractor) Extract(r *http.Request, overrides ...OverrideOptions) (Extraction, error)
- func (e *Extractor) ExtractAddr(r *http.Request, overrides ...OverrideOptions) (netip.Addr, error)
- func (e *Extractor) ExtractAddrFrom(input RequestInput, overrides ...OverrideOptions) (netip.Addr, error)
- func (e *Extractor) ExtractFrom(input RequestInput, overrides ...OverrideOptions) (Extraction, error)
- type HeaderValues
- type HeaderValuesFunc
- type InvalidIPError
- type Logger
- type Metrics
- type MultipleHeadersError
- type Option
- func AllowPrivateIPs(allow bool) Option
- func AllowReservedClientPrefixes(prefixes ...netip.Prefix) Option
- func MaxChainLength(max int) Option
- func MaxTrustedProxies(max int) Option
- func MinTrustedProxies(min int) Option
- func PresetDirectConnection() Option
- func PresetLoopbackReverseProxy() Option
- func PresetPreferredHeaderThenXFFLax(header string) Option
- func PresetVMReverseProxy() Option
- func Priority(sources ...string) Option
- func TrustLocalProxyDefaults() Option
- func TrustLoopbackProxy() Option
- func TrustPrivateProxyRanges() Option
- func TrustProxyAddrs(addrs ...netip.Addr) Option
- func TrustProxyPrefixes(prefixes ...netip.Prefix) Option
- func WithChainSelection(selection ChainSelection) Option
- func WithDebugInfo(enable bool) Option
- func WithLogger(logger Logger) Option
- func WithMetrics(metrics Metrics) Option
- func WithMetricsFactory(factory func() (Metrics, error)) Option
- func WithSecurityMode(mode SecurityMode) Option
- type OverrideOptions
- type ProxyValidationError
- type RemoteAddrError
- type RequestInput
- type SecurityMode
- type SetValue
Examples ¶
Constants ¶
const ( // SourceForwarded resolves from the RFC7239 Forwarded header. SourceForwarded = "forwarded" // SourceXForwardedFor resolves from the X-Forwarded-For header. SourceXForwardedFor = "x_forwarded_for" // SourceXRealIP resolves from the X-Real-IP header. SourceXRealIP = "x_real_ip" // SourceRemoteAddr resolves from Request.RemoteAddr. SourceRemoteAddr = "remote_addr" )
const ( // DefaultMaxChainLength is the maximum number of IPs allowed in a proxy // chain. This prevents DoS attacks using extremely long header values that // could cause excessive memory allocation or CPU usage during parsing. 100 // is chosen as a reasonable upper bound that accommodates complex // multi-region, multi-CDN setups while still providing protection. Typical // proxy chains rarely exceed 5-10 entries. DefaultMaxChainLength = 100 )
Variables ¶
var ( // ErrNoTrustedProxies indicates no trusted proxies were found in a parsed // chain when at least one is required. ErrNoTrustedProxies = errors.New("no trusted proxies found in proxy chain") // request. ErrSourceUnavailable = errors.New("source unavailable") // ErrMultipleSingleIPHeaders indicates multiple values were provided for a // single-IP header source. ErrMultipleSingleIPHeaders = errors.New("multiple single-IP headers received") // ErrUntrustedProxy indicates a header source was provided by an untrusted // immediate proxy. ErrUntrustedProxy = errors.New("request from untrusted proxy") // ErrTooFewTrustedProxies indicates trusted proxies in the chain are below // the configured minimum. ErrTooFewTrustedProxies = errors.New("too few trusted proxies in proxy chain") // ErrTooManyTrustedProxies indicates trusted proxies in the chain exceed the // configured maximum. ErrTooManyTrustedProxies = errors.New("too many trusted proxies in proxy chain") // ErrInvalidIP indicates the extracted client IP is invalid or implausible. ErrInvalidIP = errors.New("invalid or implausible IP address") // ErrChainTooLong indicates a Forwarded/X-Forwarded-For chain exceeded the // configured maximum length. ErrChainTooLong = errors.New("proxy chain too long") // ErrInvalidForwardedHeader indicates a malformed RFC7239 Forwarded header. ErrInvalidForwardedHeader = errors.New("invalid Forwarded header") )
Functions ¶
func ExtractAddrFromWithOptions ¶ added in v0.0.6
func ExtractAddrFromWithOptions(input RequestInput, opts ...Option) (netip.Addr, error)
ExtractAddrFromWithOptions is a one-shot convenience helper.
It constructs a temporary extractor from opts and resolves only the client IP address from framework-agnostic request input.
func ExtractAddrWithOptions ¶ added in v0.0.4
ExtractAddrWithOptions is a one-shot convenience helper.
It constructs a temporary extractor from opts and resolves only the client IP address for r.
func NormalizeSourceName ¶
NormalizeSourceName canonicalizes a source/header name for reporting.
It lowercases the value and replaces hyphens with underscores.
Types ¶
type ChainDebugInfo ¶
ChainDebugInfo describes parsed chain-analysis details for diagnostics.
type ChainSelection ¶ added in v0.0.4
type ChainSelection int
ChainSelection controls how the client candidate is selected from a parsed proxy chain after trusted proxy validation.
const ( // Start at 1 to avoid zero-value confusion and make invalid selections // explicit. // // RightmostUntrustedIP selects the rightmost untrusted address in the chain. RightmostUntrustedIP ChainSelection = iota + 1 // LeftmostUntrustedIP selects the leftmost untrusted address in the chain. LeftmostUntrustedIP )
func (ChainSelection) String ¶ added in v0.0.4
func (s ChainSelection) String() string
String returns the canonical text representation of s.
type ChainTooLongError ¶
type ChainTooLongError struct {
ExtractionError
ChainLength int
MaxLength int
}
ChainTooLongError reports an overlong Forwarded/X-Forwarded-For chain.
func (*ChainTooLongError) Error ¶
func (e *ChainTooLongError) Error() string
Error implements error.
type Extraction ¶ added in v0.0.4
type Extraction struct {
IP netip.Addr
Source string
TrustedProxyCount int
DebugInfo *ChainDebugInfo
}
Extraction contains extraction metadata.
On error, Source may still be set when available.
For additional diagnostics (such as chain details or trusted-proxy counts), inspect typed errors like ProxyValidationError and InvalidIPError.
func ExtractFromWithOptions ¶ added in v0.0.6
func ExtractFromWithOptions(input RequestInput, opts ...Option) (Extraction, error)
ExtractFromWithOptions is a one-shot convenience helper.
It constructs a temporary extractor from opts and resolves metadata from framework-agnostic request input.
func ExtractWithOptions ¶ added in v0.0.4
func ExtractWithOptions(r *http.Request, opts ...Option) (Extraction, error)
ExtractWithOptions is a one-shot convenience helper.
It constructs a temporary extractor from opts and resolves metadata for r.
type ExtractionError ¶
ExtractionError wraps a source-specific extraction failure.
func (*ExtractionError) SourceName ¶
func (e *ExtractionError) SourceName() string
SourceName returns the source identifier associated with this error.
func (*ExtractionError) Unwrap ¶
func (e *ExtractionError) Unwrap() error
Unwrap returns the underlying sentinel or wrapped error.
type Extractor ¶
type Extractor struct {
// contains filtered or unexported fields
}
Extractor resolves client IP information from HTTP requests and framework-agnostic request inputs.
Extractor instances are safe for concurrent reuse.
func New ¶
New creates an Extractor from one or more Option builders.
Example (Cloudflare) ¶
package main
import (
"fmt"
"net/http"
"github.com/abczzz13/clientip"
)
func main() {
extractor, _ := clientip.New(
clientip.TrustLoopbackProxy(),
clientip.Priority("CF-Connecting-IP", clientip.SourceXForwardedFor, clientip.SourceRemoteAddr),
)
req := &http.Request{RemoteAddr: "127.0.0.1:12345", Header: make(http.Header)}
req.Header.Set("CF-Connecting-IP", "1.1.1.1")
extraction, _ := extractor.Extract(req)
fmt.Printf("Client IP: %s (from %s)\n", extraction.IP, extraction.Source)
}
Example (FlexibleProxyRange) ¶
package main
import (
"fmt"
"net/http"
"net/netip"
"github.com/abczzz13/clientip"
)
func main() {
cidrs, _ := netip.ParsePrefix("10.0.0.0/8")
extractor, _ := clientip.New(
clientip.TrustProxyPrefixes(cidrs),
clientip.MinTrustedProxies(1),
clientip.MaxTrustedProxies(3),
clientip.Priority(clientip.SourceXForwardedFor, clientip.SourceRemoteAddr),
)
req1 := &http.Request{RemoteAddr: "10.0.0.1:12345", Header: make(http.Header)}
req1.Header.Set("X-Forwarded-For", "1.1.1.1, 10.0.0.1")
extraction1, _ := extractor.Extract(req1)
fmt.Printf("1 proxy: %s\n", extraction1.IP)
req2 := &http.Request{RemoteAddr: "10.0.0.3:12345", Header: make(http.Header)}
req2.Header.Set("X-Forwarded-For", "8.8.8.8, 10.0.0.2, 10.0.0.3")
extraction2, _ := extractor.Extract(req2)
fmt.Printf("2 proxies: %s\n", extraction2.IP)
}
Example (Forwarded) ¶
package main
import (
"fmt"
"net/http"
"github.com/abczzz13/clientip"
)
func main() {
extractor, _ := clientip.New(
clientip.TrustLoopbackProxy(),
clientip.Priority(clientip.SourceForwarded, clientip.SourceRemoteAddr),
)
req := &http.Request{RemoteAddr: "127.0.0.1:12345", Header: make(http.Header)}
req.Header.Set("Forwarded", "for=1.1.1.1")
extraction, _ := extractor.Extract(req)
fmt.Println(extraction.IP, extraction.Source)
}
Output: 1.1.1.1 forwarded
Example (Simple) ¶
package main
import (
"fmt"
"net/http"
"github.com/abczzz13/clientip"
)
func main() {
extractor, err := clientip.New()
if err != nil {
panic(err)
}
req := &http.Request{RemoteAddr: "8.8.4.4:12345", Header: make(http.Header)}
ip, err := extractor.ExtractAddr(req)
if err != nil {
panic(err)
}
fmt.Printf("Client IP: %s\n", ip)
}
Example (WithOptions) ¶
package main
import (
"fmt"
"log/slog"
"net/http"
"net/netip"
"os"
"github.com/abczzz13/clientip"
)
func main() {
cidrs, _ := netip.ParsePrefix("10.0.0.0/8")
extractor, err := clientip.New(
clientip.TrustProxyPrefixes(cidrs),
clientip.MinTrustedProxies(1),
clientip.MaxTrustedProxies(2),
clientip.Priority(clientip.SourceXForwardedFor, clientip.SourceRemoteAddr),
clientip.AllowPrivateIPs(false),
clientip.WithLogger(slog.New(slog.NewTextHandler(os.Stdout, nil))),
)
if err != nil {
panic(err)
}
req := &http.Request{RemoteAddr: "10.0.1.5:12345", Header: make(http.Header)}
req.Header.Set("X-Forwarded-For", "1.1.1.1, 10.0.1.5")
extraction, _ := extractor.Extract(req)
fmt.Printf("Client IP: %s from source: %s\n", extraction.IP, extraction.Source)
}
func (*Extractor) Extract ¶ added in v0.0.4
func (e *Extractor) Extract(r *http.Request, overrides ...OverrideOptions) (Extraction, error)
Extract resolves client IP and metadata for the request.
When overrides are provided, they are merged left-to-right and applied only for this call.
func (*Extractor) ExtractAddr ¶ added in v0.0.4
ExtractAddr resolves only the client IP address.
func (*Extractor) ExtractAddrFrom ¶ added in v0.0.6
func (e *Extractor) ExtractAddrFrom(input RequestInput, overrides ...OverrideOptions) (netip.Addr, error)
ExtractAddrFrom resolves only the client IP address from framework-agnostic request input.
func (*Extractor) ExtractFrom ¶ added in v0.0.6
func (e *Extractor) ExtractFrom(input RequestInput, overrides ...OverrideOptions) (Extraction, error)
ExtractFrom resolves client IP and metadata from framework-agnostic request input.
When overrides are provided, they are merged left-to-right and applied only for this call.
Example ¶
package main
import (
"context"
"fmt"
"net/textproto"
"github.com/abczzz13/clientip"
)
func main() {
extractor, _ := clientip.New(
clientip.TrustLoopbackProxy(),
clientip.Priority("CF-Connecting-IP", clientip.SourceRemoteAddr),
)
cfHeader := textproto.CanonicalMIMEHeaderKey("CF-Connecting-IP")
input := clientip.RequestInput{
Context: context.Background(),
RemoteAddr: "127.0.0.1:12345",
Path: "/framework-request",
Headers: clientip.HeaderValuesFunc(func(name string) []string {
if name == cfHeader {
return []string{"8.8.8.8"}
}
return nil
}),
}
extraction, _ := extractor.ExtractFrom(input)
fmt.Println(extraction.IP, extraction.Source)
}
Output: 8.8.8.8 cf_connecting_ip
type HeaderValues ¶ added in v0.0.6
HeaderValues provides access to request header values by name.
Implementations should return one slice entry per received header line. Single-IP sources rely on per-line values to detect duplicates, and chain sources preserve wire order across repeated lines.
Header names are requested in canonical MIME format (for example "X-Forwarded-For").
net/http's http.Header satisfies this interface directly.
type HeaderValuesFunc ¶ added in v0.0.6
HeaderValuesFunc adapts a function to the HeaderValues interface.
func (HeaderValuesFunc) Values ¶ added in v0.0.6
func (f HeaderValuesFunc) Values(name string) []string
Values implements HeaderValues.
type InvalidIPError ¶
type InvalidIPError struct {
ExtractionError
Chain string
ExtractedIP string
Index int
TrustedProxies int
}
InvalidIPError reports an invalid or implausible extracted client IP.
type Logger ¶ added in v0.0.3
Logger records security-significant events emitted by Extractor.
Implementations should be safe for concurrent use, as a single Extractor instance is typically shared across many goroutines.
The provided context comes from the inbound HTTP request and can carry tracing metadata (for example, trace or span IDs).
The interface intentionally mirrors slog's WarnContext signature, so *slog.Logger can be used directly without an adapter.
type Metrics ¶
type Metrics interface {
// RecordExtractionSuccess is called when a source successfully returns a
// client IP.
RecordExtractionSuccess(source string)
// RecordExtractionFailure is called when a source is attempted but cannot
// return a valid client IP.
RecordExtractionFailure(source string)
// RecordSecurityEvent is called when the extractor observes a
// security-relevant condition.
RecordSecurityEvent(event string)
}
Metrics records extraction outcomes and security events emitted by Extractor.
Implementations should be safe for concurrent use, as a single Extractor instance is typically shared across many goroutines.
type MultipleHeadersError ¶
type MultipleHeadersError struct {
ExtractionError
HeaderCount int
HeaderName string
RemoteAddr string
}
MultipleHeadersError reports duplicate header-line values for a source that expects a single header line.
func (*MultipleHeadersError) Error ¶
func (e *MultipleHeadersError) Error() string
Error implements error.
type Option ¶
type Option func(*config) error
Option configures an Extractor.
Construct options using package-provided option builder functions.
func AllowPrivateIPs ¶
AllowPrivateIPs configures whether private client IPs are accepted.
func AllowReservedClientPrefixes ¶ added in v0.0.6
AllowReservedClientPrefixes configures reserved client prefixes to explicitly allow.
Example ¶
package main
import (
"fmt"
"net/http"
"net/netip"
"github.com/abczzz13/clientip"
)
func main() {
extractor, _ := clientip.New(
clientip.AllowReservedClientPrefixes(netip.MustParsePrefix("198.51.100.0/24")),
)
req := &http.Request{RemoteAddr: "198.51.100.10:12345", Header: make(http.Header)}
extraction, _ := extractor.Extract(req)
fmt.Println(extraction.IP, extraction.Source)
}
Output: 198.51.100.10 remote_addr
func MaxChainLength ¶
MaxChainLength sets the maximum number of entries accepted in proxy chains.
func MaxTrustedProxies ¶ added in v0.0.6
MaxTrustedProxies sets the maximum trusted proxy count for chain-header sources.
func MinTrustedProxies ¶ added in v0.0.6
MinTrustedProxies sets the minimum trusted proxy count for chain-header sources.
func PresetDirectConnection ¶ added in v0.0.4
func PresetDirectConnection() Option
PresetDirectConnection configures extraction for direct client-to-app traffic.
This preset extracts from RemoteAddr only.
func PresetLoopbackReverseProxy ¶ added in v0.0.4
func PresetLoopbackReverseProxy() Option
PresetLoopbackReverseProxy configures extraction for apps behind a reverse proxy on the same host (for example NGINX on localhost).
It trusts loopback proxy CIDRs and uses X-Forwarded-For with RemoteAddr fallback.
func PresetPreferredHeaderThenXFFLax ¶ added in v0.0.4
PresetPreferredHeaderThenXFFLax configures extraction to prefer a single custom header, then fall back to X-Forwarded-For and RemoteAddr.
It also enables SecurityModeLax so invalid values in the preferred header can fall through to lower-priority sources.
Header-based sources still require trusted proxy CIDRs.
Example ¶
package main
import (
"fmt"
"net/http"
"github.com/abczzz13/clientip"
)
func main() {
extractor, _ := clientip.New(
clientip.TrustLoopbackProxy(),
clientip.PresetPreferredHeaderThenXFFLax("X-Frontend-IP"),
)
req := &http.Request{RemoteAddr: "127.0.0.1:12345", Header: make(http.Header)}
req.Header.Set("X-Frontend-IP", "not-an-ip")
req.Header.Set("X-Forwarded-For", "8.8.8.8")
extraction, _ := extractor.Extract(req)
fmt.Println(extraction.IP, extraction.Source)
}
Output: 8.8.8.8 x_forwarded_for
func PresetVMReverseProxy ¶ added in v0.0.4
func PresetVMReverseProxy() Option
PresetVMReverseProxy configures extraction for apps behind a reverse proxy in a typical VM or private-network setup.
It trusts loopback and private proxy CIDRs and uses X-Forwarded-For with RemoteAddr fallback.
Example ¶
package main
import (
"fmt"
"net/http"
"github.com/abczzz13/clientip"
)
func main() {
extractor, _ := clientip.New(clientip.PresetVMReverseProxy())
req := &http.Request{RemoteAddr: "127.0.0.1:12345", Header: make(http.Header)}
req.Header.Set("X-Forwarded-For", "1.1.1.1")
extraction, _ := extractor.Extract(req)
fmt.Println(extraction.IP, extraction.Source)
}
Output: 1.1.1.1 x_forwarded_for
func Priority ¶
Priority sets extraction source order.
Source names are canonicalized so built-in aliases resolve to canonical constants.
func TrustLocalProxyDefaults ¶ added in v0.0.4
func TrustLocalProxyDefaults() Option
TrustLocalProxyDefaults adds loopback and private network CIDRs.
func TrustLoopbackProxy ¶ added in v0.0.4
func TrustLoopbackProxy() Option
TrustLoopbackProxy adds loopback CIDRs to trusted proxy ranges.
func TrustPrivateProxyRanges ¶ added in v0.0.4
func TrustPrivateProxyRanges() Option
TrustPrivateProxyRanges adds private network CIDRs to trusted proxy ranges.
func TrustProxyAddrs ¶ added in v0.0.6
TrustProxyAddrs adds trusted upstream proxy host addresses.
func TrustProxyPrefixes ¶ added in v0.0.6
TrustProxyPrefixes adds trusted proxy network prefixes.
func WithChainSelection ¶ added in v0.0.4
func WithChainSelection(selection ChainSelection) Option
WithChainSelection sets how client candidates are chosen from chain headers.
Example (LeftmostUntrusted) ¶
package main
import (
"fmt"
"net/http"
"net/netip"
"github.com/abczzz13/clientip"
)
func main() {
cloudflareCIDRs, _ := netip.ParsePrefix("173.245.48.0/20")
extractor, _ := clientip.New(
clientip.TrustProxyPrefixes(cloudflareCIDRs),
clientip.MinTrustedProxies(1),
clientip.MaxTrustedProxies(3),
clientip.Priority(clientip.SourceXForwardedFor, clientip.SourceRemoteAddr),
clientip.WithChainSelection(clientip.LeftmostUntrustedIP),
)
req := &http.Request{RemoteAddr: "173.245.48.5:443", Header: make(http.Header)}
req.Header.Set("X-Forwarded-For", "1.1.1.1, 173.245.48.5")
ip, _ := extractor.ExtractAddr(req)
fmt.Printf("Client IP: %s\n", ip)
}
func WithDebugInfo ¶
WithDebugInfo controls whether chain-debug metadata is included in results.
func WithLogger ¶
WithLogger sets the logger implementation used for warning events.
func WithMetrics ¶
WithMetrics sets a concrete metrics implementation.
If previously configured, a metrics factory is disabled.
func WithMetricsFactory ¶ added in v0.0.4
WithMetricsFactory configures a lazy metrics constructor.
The factory is invoked only for the final winning metrics option after option validation succeeds.
func WithSecurityMode ¶
func WithSecurityMode(mode SecurityMode) Option
WithSecurityMode sets strict or lax fallback behavior after security errors.
Example (Lax) ¶
package main
import (
"fmt"
"net/http"
"net/netip"
"github.com/abczzz13/clientip"
)
func main() {
extractor, _ := clientip.New(
clientip.TrustProxyAddrs(netip.MustParseAddr("1.1.1.1")),
clientip.Priority(clientip.SourceForwarded, clientip.SourceRemoteAddr),
clientip.WithSecurityMode(clientip.SecurityModeLax),
)
req := &http.Request{RemoteAddr: "1.1.1.1:12345", Header: make(http.Header)}
req.Header.Set("Forwarded", `for="1.1.1.1`)
extraction, _ := extractor.Extract(req)
fmt.Println(extraction.IP, extraction.Source)
}
Output: 1.1.1.1 remote_addr
Example (Strict) ¶
package main
import (
"errors"
"fmt"
"net/http"
"net/netip"
"github.com/abczzz13/clientip"
)
func main() {
extractor, _ := clientip.New(
clientip.TrustProxyAddrs(netip.MustParseAddr("1.1.1.1")),
clientip.Priority(clientip.SourceForwarded, clientip.SourceRemoteAddr),
clientip.WithSecurityMode(clientip.SecurityModeStrict),
)
req := &http.Request{RemoteAddr: "1.1.1.1:12345", Header: make(http.Header)}
req.Header.Set("Forwarded", `for="1.1.1.1`)
extraction, err := extractor.Extract(req)
fmt.Println(err == nil, errors.Is(err, clientip.ErrInvalidForwardedHeader), extraction.Source)
}
Output: false true forwarded
type OverrideOptions ¶ added in v0.0.4
type OverrideOptions struct {
TrustedProxyPrefixes SetValue[[]netip.Prefix]
MinTrustedProxies SetValue[int]
MaxTrustedProxies SetValue[int]
AllowPrivateIPs SetValue[bool]
AllowReservedClientPrefixes SetValue[[]netip.Prefix]
MaxChainLength SetValue[int]
ChainSelection SetValue[ChainSelection]
SecurityMode SetValue[SecurityMode]
DebugInfo SetValue[bool]
SourcePriority SetValue[[]string]
}
OverrideOptions applies per-call policy overrides.
Only policy-related fields are overrideable. Logger and Metrics remain fixed at extractor construction time.
type ProxyValidationError ¶
type ProxyValidationError struct {
ExtractionError
Chain string
TrustedProxyCount int
MinTrustedProxies int
MaxTrustedProxies int
}
ProxyValidationError reports failures from trusted-proxy chain validation.
func (*ProxyValidationError) Error ¶
func (e *ProxyValidationError) Error() string
Error implements error.
type RemoteAddrError ¶
type RemoteAddrError struct {
ExtractionError
RemoteAddr string
}
RemoteAddrError reports an invalid or implausible Request.RemoteAddr value.
type RequestInput ¶ added in v0.0.6
type RequestInput struct {
Context context.Context
RemoteAddr string
Path string
Headers HeaderValues
}
RequestInput provides framework-agnostic request data for extraction.
Context defaults to context.Background() when nil.
For Headers, preserve repeated header lines as separate values for each header name (for example two X-Forwarded-For lines should yield a slice with length 2, and two X-Real-IP lines should also yield length 2).
type SecurityMode ¶
type SecurityMode int
SecurityMode controls fallback behavior after security-significant errors.
const ( // SecurityModeStrict fails closed and stops on security-significant errors. SecurityModeStrict SecurityMode = iota + 1 // SecurityModeLax allows fallback to lower-priority sources after such errors. SecurityModeLax )
func (SecurityMode) String ¶
func (m SecurityMode) String() string
String returns the canonical text representation of m.