caddyserver/caddy · error

unexpected proxy protocol version

Error message

unexpected proxy protocol version

What it means

After a successful proxy-protocol info lookup, the transport switches on the configured `proxy_protocol` version and only accepts "v1" or "v2". Any other value falls to default and returns this error from the dialer. In practice only reachable via hand-written JSON, since the Caddyfile adapter validates the token.

Source

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

			// 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{
					IP: net.IPv4zero,
				}
			case proxyProtocolInfo.AddrPort.Addr().Is6():
				destAddr = &net.TCPAddr{
					IP: net.IPv6zero,
				}
			default:
				return nil, fmt.Errorf("unexpected remote addr type in proxy protocol info")
			}

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Set proxy_protocol to exactly "v1" or "v2" (string with the leading v)
  2. Prefer authoring in Caddyfile and adapting with `caddy adapt` so invalid tokens are caught at adapt time

Example fix

// JSON before
"transport": {"protocol": "http", "proxy_protocol": "2"}
// JSON after
"transport": {"protocol": "http", "proxy_protocol": "v2"}
Defensive patterns

Strategy: validation

Validate before calling

switch h.ProxyProtocol {
case "", "v1", "v2":
default:
	return fmt.Errorf("proxy_protocol must be v1 or v2, got %q", h.ProxyProtocol)
}

Prevention

When it happens

Trigger: JSON config with transport.proxy_protocol set to something like "v3", "2", or an empty-but-present value; custom config generators that emit the raw field without validating it.

Common situations: Programmatic config generation or hand-edited JSON where the Caddyfile's syntax validation is bypassed.

Related errors


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