gofiber/fiber · error
proxy: WithClient requires a non-nil *fasthttp.Client
Error message
proxy: WithClient requires a non-nil *fasthttp.Client
What it means
proxy.WithClient installs a user-supplied *fasthttp.Client as the package-level proxy client and wires the dial-time SSRF guard onto it via ConfigureClient. Passing nil has no valid meaning (there is no "unset" — simply omit the call to keep the default guarded client), so WithClient panics immediately. The guard is installed before the client is stored, so any later proxy.Do/Forward/DomainForward/BalancerForward dispatch through it re-validates the resolved IP at connect time.
Solutions
- Construct the *fasthttp.Client before calling WithClient, and only call it when the reference is non-nil.
- If you have no custom client requirements, do not call WithClient at all — the package default client is already guarded.
- Guard the call site: if cli != nil { proxy.WithClient(cli) }.
Example fix
// before
var cli *fasthttp.Client
if useCustom {
cli = buildClient()
}
proxy.WithClient(cli) // panics when useCustom is false
// after
if cli != nil {
proxy.WithClient(cli)
} Defensive patterns
Strategy: validation
Validate before calling
func registerProxyClient(cli *fasthttp.Client) {
if cli == nil {
log.Println("proxy: skipping WithClient, client is nil")
return
}
proxy.WithClient(cli)
} Type guard
func isNonNilClient(cli *fasthttp.Client) bool { return cli != nil } Prevention
- Treat *fasthttp.Client construction as fallible and check the result before registering.
- Default to not calling WithClient at all unless you need custom client behavior.
- Add a unit test asserting WithClient is only reachable through a non-nil guard.
When it happens
Trigger: proxy.WithClient(nil) — typically a variable that was never initialized, e.g. proxy.WithClient(cli) where cli is a nil *fasthttp.Client returned from a constructor that failed.
Common situations: Conditional client construction where the variable stays nil on an error path; a refactor that moved client creation into a helper returning nil; a test stub that forgot to instantiate the client.
Related errors
- ErrUpstreamHostBlocked
- ErrUpstreamHostInvalid
- nil handler in route
- Servers cannot be empty
- add: invalid http method
AI-assisted analysis of gofiber/fiber@a105acad6c (2026-08-11).
Data as JSON: /api/errors/253422dc46cd7629.
Report an issue: GitHub.
Appendix: source
Thrown at middleware/proxy/proxy.go:234
return // already guarded (directly or composed with a user hook)
}
cli.ConfigureClient = (&guardedConfigureClient{orig: existing}).run
}
func init() {
ensureClientGuarded(defaultClient)
client.Store(defaultClient)
}
// WithClient sets the global proxy client.
// This function should be called before Do and Forward — doing so installs
// the dial-time SSRF guard (via the client's ConfigureClient hook,
// composing with any hook it already carries) before the client dials any
// host, so requests dispatched through it re-validate the resolved IP at
// connect time, matching the default client's behavior.
func WithClient(cli *fasthttp.Client) {
if cli == nil {
panic("proxy: WithClient requires a non-nil *fasthttp.Client")
}
ensureClientGuarded(cli)
client.Store(cli)
}
// realIPHeader is the field the proxy overwrites with the peer address Fiber
// derived, so an upstream reading it sees this hop's view rather than the
// client's claim.
const realIPHeader = "X-Real-IP"
// setRealIP replaces every inbound X-Real-IP field line with the peer address
// Fiber derived. Set alone overwrites the first and leaves the rest, so a client
// sending it twice kept a value of its own on the wire.
func setRealIP(c fiber.Ctx) {
// Resolve the address before deleting anything: with ProxyHeader set to
// "X-Real-IP", c.IP() reads the very header being replaced, and deleting first
// handed the upstream an empty value.View on GitHub (pinned to a105acad6c)