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.Distributed

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Implement ProxyFunc() func(*http.Request) (*url.URL, error) on the custom module type (i.e. satisfy caddy.ProxyFuncProducer).
  2. 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.
  3. Rebuild the plugin against the same Caddy version the server runs, since interface signatures changed across releases.
  4. 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

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


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/1bd8d16ad6d47d9a. Report an issue: GitHub.