caddyserver/caddy · error
network_proxy module is not `(func(*http.Request) (*url.URL,
Error message
network_proxy module is not `(func(*http.Request) (*url.URL, error))``
What it means
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.
Source
Thrown at modules/caddytls/acmeissuer.go:276
Email: iss.Email,
Profile: iss.Profile,
AccountKeyPEM: iss.AccountKey,
CertObtainTimeout: time.Duration(iss.ACMETimeout),
TrustedRoots: iss.rootPool,
ExternalAccount: iss.ExternalAccount,
NotAfter: time.Duration(iss.CertificateLifetime),
Logger: iss.logger,
}
if len(iss.NetworkProxyRaw) != 0 {
proxyMod, err := ctx.LoadModule(iss, "NetworkProxyRaw")
if err != nil {
return template, fmt.Errorf("failed to load network_proxy module: %v", err)
}
if m, ok := proxyMod.(caddy.ProxyFuncProducer); ok {
template.HTTPProxy = m.ProxyFunc()
} else {
return template, fmt.Errorf("network_proxy module is not `(func(*http.Request) (*url.URL, error))``")
}
}
if iss.Challenges != nil {
if iss.Challenges.HTTP != nil {
template.DisableHTTPChallenge = iss.Challenges.HTTP.Disabled
template.AltHTTPPort = iss.Challenges.HTTP.AlternatePort
}
if iss.Challenges.TLSALPN != nil {
template.DisableTLSALPNChallenge = iss.Challenges.TLSALPN.Disabled
template.AltTLSALPNPort = iss.Challenges.TLSALPN.AlternatePort
}
if iss.Challenges.DNS != nil {
template.DNS01Solver = iss.Challenges.DNS.solver
}
template.ListenHost = iss.Challenges.BindHost
if iss.Challenges.Distributed != nil {
template.DisableDistributedSolvers = !*iss.Challenges.DistributedView on GitHub (pinned to 50e54ee279)
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).
Example fix
// before: module lacks the interface
type MyProxy struct{}
func (MyProxy) CaddyModule() caddy.ModuleInfo { /* registered as network_proxy */ }
// after
var _ caddy.ProxyFuncProducer = (*MyProxy)(nil)
func (m *MyProxy) ProxyFunc() func(*http.Request) (*url.URL, error) {
return http.ProxyFromEnvironment
} Defensive patterns
Strategy: type-guard
Validate before calling
// Before registering, ensure the module satisfies the producer interface.
// In tests: load the module the way caddy.Context would and assert the type.
proxyMod, err := ctx.LoadModule(iss, "NetworkProxyRaw")
if err == nil {
if _, ok := proxyMod.(caddy.ProxyFuncProducer); !ok {
// reject config before the error path
return fmt.Errorf("module %T is not a ProxyFuncProducer", proxyMod)
}
} Type guard
func isProxyFuncProducer(m caddy.Module) bool {
_, ok := m.(caddy.ProxyFuncProducer)
return ok
} Try / catch
if err := iss.Provision(ctx); err != nil && strings.Contains(err.Error(), "network_proxy module is not") {
log.Printf("custom network_proxy plugin incompatible; falling back to environment proxy")
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- loading DNS provider module: %v
- failed to load network_proxy module: %v
- loading TLS automation management module: %s
- filling in global issuer defaults for AP %d, issuer %d: %v
- acme_dns specified without DNS provider config, but no provi
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/1bd8d16ad6d47d9a.
Report an issue: GitHub.