SRouter records a client IP in SRouterContext for logging and rate limiting. Configure its source with RouterConfig.IPConfig.
When RouterConfig.IPConfig is nil, the router ignores proxy headers and uses http.Request.RemoteAddr, with a valid host port removed:
config := router.RouterConfig{
IPConfig: nil, // use the immediate peer address
}This is appropriate when the service is internet-facing or when the immediate peer address is the value you need.
Use a header source only when every request reaches the application through infrastructure that overwrites or sanitizes that header:
config := router.RouterConfig{
IPConfig: &router.IPConfig{
Source: router.IPSourceXForwardedFor,
TrustProxy: true,
},
}The available sources are:
IPSourceRemoteAddr: the immediate peer inRemoteAddr.IPSourceXForwardedFor: the rightmost non-empty entry inX-Forwarded-For.IPSourceXRealIP: the completeX-Real-IPvalue.IPSourceCustomHeader: the complete value ofCustomHeader.
For example, given:
X-Forwarded-For: client-supplied, client-seen-by-edge, edge-seen-by-app-proxy
SRouter selects edge-seen-by-app-proxy, the rightmost value appended by the
proxy nearest the application. That value describes the nearest proxy's
observed upstream peer; it is not the nearest proxy's own address. SRouter
deliberately does not select the commonly described leftmost "original client"
value, because a client can prepend arbitrary entries. If your topology has
multiple trusted proxy hops and you need a different address, normalize the
header at the last proxy before it reaches SRouter.
If IPConfig is non-nil but Source is empty or unknown, SRouter treats it as IPSourceXForwardedFor. Prefer setting Source explicitly.
- With
TrustProxy: false, SRouter ignores a configured header source and usesRemoteAddr. - With
TrustProxy: true, SRouter uses the configured header. It falls back toRemoteAddronly when the selected header is empty.
SRouter does not reject a malformed, non-empty proxy-header value. It removes a port when the value is a valid host-port pair; otherwise it preserves the value. Header validation and sanitization must therefore happen at the trusted proxy boundary.
router.DefaultIPConfig() and the standalone router.ClientIPMiddleware(nil) are different from a nil RouterConfig.IPConfig: they default to trusted X-Forwarded-For. Use that convenience default only behind a trusted proxy.
config := router.RouterConfig{
IPConfig: &router.IPConfig{
Source: router.IPSourceCustomHeader,
CustomHeader: "CF-Connecting-IP",
TrustProxy: true,
},
}Make sure requests cannot bypass the proxy and reach the application with a client-controlled value for the custom header.
Use the context helper in a handler or middleware:
clientIP, ok := scontext.GetClientIPFromRequest[string, User](req)
if !ok {
// No SRouter client information has been attached to this request.
}The type arguments must match the router's user ID and user object types.