OpenNHP/opennhp · error
loopback peer sent malformed X-Real-IP
Error message
loopback peer %s sent malformed X-Real-IP %q
What it means
Raised by realClientAddr for a loopback peer whose X-Real-IP header is present but fails net.ParseIP — the header contains a value that is not a bare IP address (e.g. 'ip:port', a hostname, or junk appended by a misconfigured proxy).
Solutions
- Set X-Real-IP to the plain client IP only (nginx: proxy_set_header X-Real-IP $remote_addr)
- Check for accidental concatenation of IP and port in the proxy template
- Reload the reverse proxy after fixing the header
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at endpoints/relay/relay.go:1172 when the library encounters an invalid state.
Common situations: See trigger scenarios.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/e46eef4ed17b8f21.
Report an issue: GitHub.
Appendix: source
Thrown at endpoints/relay/relay.go:1172
// Parse the direct TCP peer first so we always have a port.
peerHost, peerPortStr, err := net.SplitHostPort(r.RemoteAddr)
peerIP := net.IPv4zero
peerPort := 0
if err == nil {
if ip := net.ParseIP(peerHost); ip != nil {
peerIP = ip
}
_, _ = fmt.Sscanf(peerPortStr, "%d", &peerPort)
}
if peerIP.IsLoopback() {
realIP := strings.TrimSpace(r.Header.Get("X-Real-IP"))
if realIP == "" {
return nil, fmt.Errorf("loopback peer %s sent no X-Real-IP header; check reverse proxy config", r.RemoteAddr)
}
ip := net.ParseIP(realIP)
if ip == nil {
return nil, fmt.Errorf("loopback peer %s sent malformed X-Real-IP %q", r.RemoteAddr, realIP)
}
// X-Real-IP carries no port; the proxy peer's port is
// used so connection-tracking keys remain unique.
return &net.UDPAddr{IP: ip, Port: peerPort}, nil
}
return &net.UDPAddr{IP: peerIP, Port: peerPort}, nil
}
View on GitHub (pinned to 6e04ca5ff0)