{"record":{"id":"347d5669d5d1cab4","repo":"caddyserver/caddy","slug":"unexpected-proxy-protocol-version","errorCode":null,"errorMessage":"unexpected proxy protocol version","messagePattern":"unexpected proxy protocol version","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddyhttp/reverseproxy/httptransport.go","lineNumber":312,"sourceCode":"\t\t\t// identify this error as one that occurred during\n\t\t\t// dialing, which can be important when trying to\n\t\t\t// decide whether to retry a request\n\t\t\treturn nil, DialError{err}\n\t\t}\n\n\t\tif h.ProxyProtocol != \"\" {\n\t\t\tproxyProtocolInfo, ok := caddyhttp.GetVar(ctx, proxyProtocolInfoVarKey).(ProxyProtocolInfo)\n\t\t\tif !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"failed to get proxy protocol info from context\")\n\t\t\t}\n\t\t\tvar proxyv byte\n\t\t\tswitch h.ProxyProtocol {\n\t\t\tcase \"v1\":\n\t\t\t\tproxyv = 1\n\t\t\tcase \"v2\":\n\t\t\t\tproxyv = 2\n\t\t\tdefault:\n\t\t\t\treturn nil, fmt.Errorf(\"unexpected proxy protocol version\")\n\t\t\t}\n\n\t\t\t// The src and dst have to be of the same address family. As we don't know the original\n\t\t\t// dst address (it's kind of impossible to know) and this address is generally of very\n\t\t\t// little interest, we just set it to all zeros.\n\t\t\tvar destAddr net.Addr\n\t\t\tswitch {\n\t\t\tcase proxyProtocolInfo.AddrPort.Addr().Is4():\n\t\t\t\tdestAddr = &net.TCPAddr{\n\t\t\t\t\tIP: net.IPv4zero,\n\t\t\t\t}\n\t\t\tcase proxyProtocolInfo.AddrPort.Addr().Is6():\n\t\t\t\tdestAddr = &net.TCPAddr{\n\t\t\t\t\tIP: net.IPv6zero,\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\treturn nil, fmt.Errorf(\"unexpected remote addr type in proxy protocol info\")\n\t\t\t}","sourceCodeStart":294,"sourceCodeEnd":330,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddyhttp/reverseproxy/httptransport.go#L294-L330","documentation":"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.","triggerScenarios":"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.","commonSituations":"Programmatic config generation or hand-edited JSON where the Caddyfile's syntax validation is bypassed.","solutions":["Set proxy_protocol to exactly \"v1\" or \"v2\" (string with the leading v)","Prefer authoring in Caddyfile and adapting with `caddy adapt` so invalid tokens are caught at adapt time"],"exampleFix":"// JSON before\n\"transport\": {\"protocol\": \"http\", \"proxy_protocol\": \"2\"}\n// JSON after\n\"transport\": {\"protocol\": \"http\", \"proxy_protocol\": \"v2\"}","handlingStrategy":"validation","validationCode":"switch h.ProxyProtocol {\ncase \"\", \"v1\", \"v2\":\ndefault:\n\treturn fmt.Errorf(\"proxy_protocol must be v1 or v2, got %q\", h.ProxyProtocol)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Generate JSON from the Caddyfile (`caddy adapt`) instead of hand-writing it","Unit-test config templates against the accepted enum"],"tags":["reverse-proxy","proxy-protocol","json-config","validation"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}