{"record":{"id":"1bd8d16ad6d47d9a","repo":"caddyserver/caddy","slug":"network-proxy-module-is-not-func-http-request-1bd8d1","errorCode":null,"errorMessage":"network_proxy module is not `(func(*http.Request) (*url.URL, error))``","messagePattern":"network_proxy module is not `\\(func\\(\\*http\\.Request\\) \\(\\*url\\.URL, error\\)\\)``","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddytls/acmeissuer.go","lineNumber":276,"sourceCode":"\t\tEmail:             iss.Email,\n\t\tProfile:           iss.Profile,\n\t\tAccountKeyPEM:     iss.AccountKey,\n\t\tCertObtainTimeout: time.Duration(iss.ACMETimeout),\n\t\tTrustedRoots:      iss.rootPool,\n\t\tExternalAccount:   iss.ExternalAccount,\n\t\tNotAfter:          time.Duration(iss.CertificateLifetime),\n\t\tLogger:            iss.logger,\n\t}\n\n\tif len(iss.NetworkProxyRaw) != 0 {\n\t\tproxyMod, err := ctx.LoadModule(iss, \"NetworkProxyRaw\")\n\t\tif err != nil {\n\t\t\treturn template, fmt.Errorf(\"failed to load network_proxy module: %v\", err)\n\t\t}\n\t\tif m, ok := proxyMod.(caddy.ProxyFuncProducer); ok {\n\t\t\ttemplate.HTTPProxy = m.ProxyFunc()\n\t\t} else {\n\t\t\treturn template, fmt.Errorf(\"network_proxy module is not `(func(*http.Request) (*url.URL, error))``\")\n\t\t}\n\t}\n\n\tif iss.Challenges != nil {\n\t\tif iss.Challenges.HTTP != nil {\n\t\t\ttemplate.DisableHTTPChallenge = iss.Challenges.HTTP.Disabled\n\t\t\ttemplate.AltHTTPPort = iss.Challenges.HTTP.AlternatePort\n\t\t}\n\t\tif iss.Challenges.TLSALPN != nil {\n\t\t\ttemplate.DisableTLSALPNChallenge = iss.Challenges.TLSALPN.Disabled\n\t\t\ttemplate.AltTLSALPNPort = iss.Challenges.TLSALPN.AlternatePort\n\t\t}\n\t\tif iss.Challenges.DNS != nil {\n\t\t\ttemplate.DNS01Solver = iss.Challenges.DNS.solver\n\t\t}\n\t\ttemplate.ListenHost = iss.Challenges.BindHost\n\t\tif iss.Challenges.Distributed != nil {\n\t\t\ttemplate.DisableDistributedSolvers = !*iss.Challenges.Distributed","sourceCodeStart":258,"sourceCodeEnd":294,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddytls/acmeissuer.go#L258-L294","documentation":"Thrown while building the ACMEIssuer template when a module loaded from the issuer's network_proxy setting does not implement caddy.ProxyFuncProducer. The loaded module's ProxyFunc() is what Caddy uses as the http.Transport proxy function for ACME challenge traffic, so a module of the wrong shape cannot be wired in. This only happens with third-party/custom modules, since the built-in http proxy module implements the interface.","triggerScenarios":"Configuring the ACME issuer's network_proxy with a custom module whose caddy.ModuleInfo ID is registered under the network_proxy namespace (or whose JSON ends up in NetworkProxyRaw) but which does not have a ProxyFunc() (func(*http.Request) (*url.URL, error)) method. ctx.LoadModule succeeds, but the type assertion proxyMod.(caddy.ProxyFuncProducer) fails.","commonSituations":"Writing a custom Caddy plugin meant to route ACME traffic through a proxy but forgetting to implement ProxyFunc; registering a module under the wrong namespace so it gets picked up by network_proxy; version skew where a plugin was built against an older Caddy API surface.","solutions":["Implement ProxyFunc() func(*http.Request) (*url.URL, error) on the custom module type (i.e. satisfy caddy.ProxyFuncProducer).","Verify the module ID in CaddyModule() matches the intended namespace (e.g. caddy.network_proxy for the standard one) so the right module is loaded.","Rebuild the plugin against the same Caddy version the server runs, since interface signatures changed across releases.","As a workaround, drop the custom network_proxy module and use Caddy's built-in proxy support (HTTP_PROXY/HTTPS_PROXY env or the standard network_proxy module)."],"exampleFix":"// before: module lacks the interface\ntype MyProxy struct{}\nfunc (MyProxy) CaddyModule() caddy.ModuleInfo { /* registered as network_proxy */ }\n\n// after\nvar _ caddy.ProxyFuncProducer = (*MyProxy)(nil)\nfunc (m *MyProxy) ProxyFunc() func(*http.Request) (*url.URL, error) {\n    return http.ProxyFromEnvironment\n}","handlingStrategy":"type-guard","validationCode":"// Before registering, ensure the module satisfies the producer interface.\n// In tests: load the module the way caddy.Context would and assert the type.\nproxyMod, err := ctx.LoadModule(iss, \"NetworkProxyRaw\")\nif err == nil {\n    if _, ok := proxyMod.(caddy.ProxyFuncProducer); !ok {\n        // reject config before the error path\n        return fmt.Errorf(\"module %T is not a ProxyFuncProducer\", proxyMod)\n    }\n}","typeGuard":"func isProxyFuncProducer(m caddy.Module) bool {\n    _, ok := m.(caddy.ProxyFuncProducer)\n    return ok\n}","tryCatchPattern":"if err := iss.Provision(ctx); err != nil && strings.Contains(err.Error(), \"network_proxy module is not\") {\n    log.Printf(\"custom network_proxy plugin incompatible; falling back to environment proxy\")\n}","preventionTips":["Add a compile-time interface guard `var _ caddy.ProxyFuncProducer = (*MyProxy)(nil)` in every custom proxy plugin.","Pin plugin builds to the same Caddy version you deploy.","Run `caddy validate --config` in CI to catch module-shape errors before deployment."],"tags":["tls","acme","proxy","module-system","plugin"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}