{"id":"253422dc46cd7629","repo":"gofiber/fiber","slug":"proxy-withclient-requires-a-non-nil-fasthttp-cli","errorCode":null,"errorMessage":"proxy: WithClient requires a non-nil *fasthttp.Client","messagePattern":"proxy: WithClient requires a non-nil \\*fasthttp\\.Client","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"middleware/proxy/proxy.go","lineNumber":230,"sourceCode":"\t\treturn // already guarded (directly or composed with a user hook)\n\t}\n\tcli.ConfigureClient = (&guardedConfigureClient{orig: existing}).run\n}\n\nfunc init() {\n\tensureClientGuarded(defaultClient)\n\tclient.Store(defaultClient)\n}\n\n// WithClient sets the global proxy client.\n// This function should be called before Do and Forward — doing so installs\n// the dial-time SSRF guard (via the client's ConfigureClient hook,\n// composing with any hook it already carries) before the client dials any\n// host, so requests dispatched through it re-validate the resolved IP at\n// connect time, matching the default client's behavior.\nfunc WithClient(cli *fasthttp.Client) {\n\tif cli == nil {\n\t\tpanic(\"proxy: WithClient requires a non-nil *fasthttp.Client\")\n\t}\n\n\tensureClientGuarded(cli)\n\tclient.Store(cli)\n}\n\n// Forward performs the given http request and fills the given http response.\n// This method will return a fiber.Handler\n//\n// SSRF note: Forward validates the upstream host against the active\n// SecurityPolicy up front and, when AllowPrivateIPs is false, re-validates\n// the resolved IP at dial time via the guard installed on the dispatching\n// client, so a rebinding-capable resolver cannot swap a public answer for\n// a private one between validation and connection.\nfunc Forward(addr string, clients ...*fasthttp.Client) fiber.Handler {\n\treturn func(c fiber.Ctx) error {\n\t\tc.Request().Header.Set(\"X-Real-IP\", c.IP())\n\t\treturn Do(c, addr, clients...)","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/gofiber/fiber/blob/9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c/middleware/proxy/proxy.go#L212-L248","documentation":"proxy.WithClient installs a global *fasthttp.Client used to dispatch proxied requests and installs an SSRF guard on its dial path. Passing nil leaves no client to dispatch through, so WithClient panics. The guard requires a concrete client to attach to.","triggerScenarios":"Calling proxy.WithClient(nil), or passing a client variable that was never initialized (e.g. returned nil from a constructor that errored, with the error ignored).","commonSituations":"Initializing a client conditionally and passing the zero-value pointer. Refactoring and removing the client creation but keeping the WithClient call. A factory function returns (nil, err) and only the nil is passed.","solutions":["Construct a *fasthttp.Client with fasthttp.New() or &fasthttp.Client{} and pass that instance.","Check the client for nil before calling WithClient.","Handle any error from the client factory before passing its result."],"exampleFix":"// before\nproxy.WithClient(nil)\n// after\ncli := &fasthttp.Client{}\nproxy.WithClient(cli)","handlingStrategy":"type-guard","validationCode":"if cli == nil {\n    log.Fatal(\"proxy: WithClient requires a non-nil *fasthttp.Client\")\n}\nproxy.WithClient(cli)","typeGuard":"func isValidProxyClient(cli *fasthttp.Client) bool {\n    return cli != nil\n}","tryCatchPattern":null,"preventionTips":["Always initialize the client (e.g. &fasthttp.Client{}) before passing to WithClient.","Handle constructor errors so a nil client never reaches WithClient."],"tags":["proxy","config","ssrf","nil-check","panic"],"analyzedSha":"9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c","analyzedAt":"2026-08-04T21:44:03.395Z","schemaVersion":2}