{"record":{"id":"082db7bbd639d7a5","repo":"OpenNHP/opennhp","slug":"loopback-peer-s-sent-no-x-real-ip-header-check-r","errorCode":null,"errorMessage":"loopback peer %s sent no X-Real-IP header; check reverse proxy config","messagePattern":"loopback peer (.+?) sent no X-Real-IP header; check reverse proxy config","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"endpoints/relay/relay.go","lineNumber":1168,"sourceCode":"// which the server's isRoutablePublicIP check rejects, producing\n// silent 504s that are hard to diagnose. A loud error here points\n// operators at the misconfigured reverse proxy instead.\nfunc realClientAddr(r *http.Request) (*net.UDPAddr, error) {\n\t// Parse the direct TCP peer first so we always have a port.\n\tpeerHost, peerPortStr, err := net.SplitHostPort(r.RemoteAddr)\n\tpeerIP := net.IPv4zero\n\tpeerPort := 0\n\tif err == nil {\n\t\tif ip := net.ParseIP(peerHost); ip != nil {\n\t\t\tpeerIP = ip\n\t\t}\n\t\t_, _ = fmt.Sscanf(peerPortStr, \"%d\", &peerPort)\n\t}\n\n\tif peerIP.IsLoopback() {\n\t\trealIP := strings.TrimSpace(r.Header.Get(\"X-Real-IP\"))\n\t\tif realIP == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"loopback peer %s sent no X-Real-IP header; check reverse proxy config\", r.RemoteAddr)\n\t\t}\n\t\tip := net.ParseIP(realIP)\n\t\tif ip == nil {\n\t\t\treturn nil, fmt.Errorf(\"loopback peer %s sent malformed X-Real-IP %q\", r.RemoteAddr, realIP)\n\t\t}\n\t\t// X-Real-IP carries no port; the proxy peer's port is\n\t\t// used so connection-tracking keys remain unique.\n\t\treturn &net.UDPAddr{IP: ip, Port: peerPort}, nil\n\t}\n\n\treturn &net.UDPAddr{IP: peerIP, Port: peerPort}, nil\n}\n","sourceCodeStart":1150,"sourceCodeEnd":1181,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/relay/relay.go#L1150-L1181","documentation":"Raised by realClientAddr when an HTTP request arrives from a loopback peer (the reverse proxy on the same host) but carries no X-Real-IP header. Without it the client address would default to the loopback address, which the server's isRoutablePublicIP check rejects downstream, producing hard-to-diagnose silent 504s — hence this loud, early error pointing at the reverse proxy configuration.","triggerScenarios":"Thrown at endpoints/relay/relay.go:1168 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Configure the reverse proxy (nginx/caddy) to set X-Real-IP $remote_addr on proxied requests","Make sure the proxy header is passed to the relay's HTTP handler and not stripped","Restart/reload the proxy after the change"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"6e04ca5ff03222a699c24205cd4bf8fee9af7ffe","analyzedAt":"2026-09-07T15:44:59.941Z","contentChangedAt":"2026-09-07T15:44:59.941Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}