{"record":{"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":234,"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// realIPHeader is the field the proxy overwrites with the peer address Fiber\n// derived, so an upstream reading it sees this hop's view rather than the\n// client's claim.\nconst realIPHeader = \"X-Real-IP\"\n\n// setRealIP replaces every inbound X-Real-IP field line with the peer address\n// Fiber derived. Set alone overwrites the first and leaves the rest, so a client\n// sending it twice kept a value of its own on the wire.\nfunc setRealIP(c fiber.Ctx) {\n\t// Resolve the address before deleting anything: with ProxyHeader set to\n\t// \"X-Real-IP\", c.IP() reads the very header being replaced, and deleting first\n\t// handed the upstream an empty value.","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/gofiber/fiber/blob/a105acad6c1e4576a77f01e02973f67e962bb58d/middleware/proxy/proxy.go#L216-L252","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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) }."],"exampleFix":"// before\nvar cli *fasthttp.Client\nif useCustom {\n    cli = buildClient()\n}\nproxy.WithClient(cli) // panics when useCustom is false\n\n// after\nif cli != nil {\n    proxy.WithClient(cli)\n}","handlingStrategy":"validation","validationCode":"func registerProxyClient(cli *fasthttp.Client) {\n    if cli == nil {\n        log.Println(\"proxy: skipping WithClient, client is nil\")\n        return\n    }\n    proxy.WithClient(cli)\n}","typeGuard":"func isNonNilClient(cli *fasthttp.Client) bool { return cli != nil }","tryCatchPattern":null,"preventionTips":["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."],"tags":["proxy","config","panic","nil-safety"],"backgroundTag":null,"analyzedSha":"a105acad6c1e4576a77f01e02973f67e962bb58d","analyzedAt":"2026-08-11T17:33:26.942Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}