caddyserver/caddy · error

failed to get proxy protocol info from context

Error message

failed to get proxy protocol info from context

What it means

When `proxy_protocol` is set on the HTTP transport, the dialer expects the original client address to have been stored in the request context variable `proxyProtocolInfoVarKey` (populated by the listener's proxy_protocol wrapper). If the type assertion fails, the custom dialer returns this error and the request fails.

Source

Thrown at modules/caddyhttp/reverseproxy/httptransport.go:303

		if dialInfo, ok := GetDialInfo(ctx); ok {
			if caddyhttp.GetVar(ctx, proxyVarKey) == nil || strings.HasPrefix(dialInfo.Network, "unix") {
				network = dialInfo.Network
				address = dialInfo.Address
			}
		}

		conn, err := dialer.DialContext(ctx, network, address)
		if err != nil {
			// identify this error as one that occurred during
			// dialing, which can be important when trying to
			// decide whether to retry a request
			return nil, DialError{err}
		}

		if h.ProxyProtocol != "" {
			proxyProtocolInfo, ok := caddyhttp.GetVar(ctx, proxyProtocolInfoVarKey).(ProxyProtocolInfo)
			if !ok {
				return nil, fmt.Errorf("failed to get proxy protocol info from context")
			}
			var proxyv byte
			switch h.ProxyProtocol {
			case "v1":
				proxyv = 1
			case "v2":
				proxyv = 2
			default:
				return nil, fmt.Errorf("unexpected proxy protocol version")
			}

			// The src and dst have to be of the same address family. As we don't know the original
			// dst address (it's kind of impossible to know) and this address is generally of very
			// little interest, we just set it to all zeros.
			var destAddr net.Addr
			switch {
			case proxyProtocolInfo.AddrPort.Addr().Is4():
				destAddr = &net.TCPAddr{

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Add listener_wrappers { proxy_protocol } to the same server block so the inbound connection is decoded and client info is captured
  2. Verify ordering: listener_wrappers must come before routes in the Caddyfile site block
  3. If the client does not actually send PROXY protocol headers, either require it at the listener or remove proxy_protocol from the transport

Example fix

# before
example.com {
	reverse_proxy localhost:9000 {
		transport http {
			proxy_protocol v2
		}
	}
}
# after
example.com {
	listener_wrappers {
		proxy_protocol
	}
	reverse_proxy localhost:9000 {
		transport http {
			proxy_protocol v2
		}
	}
}
Defensive patterns

Strategy: validation

Validate before calling

// Config guard: transport proxy_protocol requires the listener wrapper
if site.HasTransportProxyProtocol() && !site.HasListenerWrapper("proxy_protocol") {
	return errors.New("add listener_wrappers { proxy_protocol } to accept PROXY protocol first")
}

Prevention

When it happens

Trigger: Enabling `transport http { proxy_protocol v2 }` on a site whose server listener does NOT have the `listener_wrappers { proxy_protocol }` wrapper, so no PROXY-protocol info ever enters the context. Also dispatching synthesized requests that bypass the listener.

Common situations: Adding proxy_protocol to the transport to forward client info to an upstream (like an LB or Cloudflare-origin setup) while forgetting that the server must first accept PROXY protocol on the inbound side.

Related errors


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