XTLS/Xray-core · warning

failed to set PROXY protocol v${xver}

Error message

failed to set PROXY protocol v${xver}

What it means

Writing the synthesized PROXY protocol header (v1 text or v2 binary depending on fb.Xver) to the fallback connection failed on its first WriteMultiBuffer. The header carries the original client's addresses so the backend sees the real source IP.

Source

Thrown at proxy/trojan/server.go:523

				if ipType == 0 {
					common.Must2(pro.Write([]byte("\x20\x00\x00\x00"))) // v2 + LOCAL + UNSPEC + UNSPEC + 0 bytes
					break
				}
				if ipType == 4 {
					common.Must2(pro.Write([]byte("\x21\x11\x00\x0C"))) // v2 + PROXY + AF_INET + STREAM + 12 bytes
					common.Must2(pro.Write(net.ParseIP(remoteAddr).To4()))
					common.Must2(pro.Write(net.ParseIP(localAddr).To4()))
				} else {
					common.Must2(pro.Write([]byte("\x21\x21\x00\x24"))) // v2 + PROXY + AF_INET6 + STREAM + 36 bytes
					common.Must2(pro.Write(net.ParseIP(remoteAddr).To16()))
					common.Must2(pro.Write(net.ParseIP(localAddr).To16()))
				}
				p1, _ := strconv.ParseUint(remotePort, 10, 16)
				p2, _ := strconv.ParseUint(localPort, 10, 16)
				common.Must2(pro.Write([]byte{byte(p1 >> 8), byte(p1), byte(p2 >> 8), byte(p2)}))
			}
			if err := serverWriter.WriteMultiBuffer(buf.MultiBuffer{pro}); err != nil {
				return errors.New("failed to set PROXY protocol v", fb.Xver).Base(err).AtWarning()
			}
		}
		if err := buf.Copy(reader, serverWriter, buf.UpdateActivity(timer)); err != nil {
			return errors.New("failed to fallback request payload").Base(err).AtInfo()
		}
		return nil
	}

	writer := buf.NewWriter(connection)

	getResponse := func() error {
		defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly)
		if err := buf.Copy(serverReader, writer, buf.UpdateActivity(timer)); err != nil {
			return errors.New("failed to deliver response payload").Base(err).AtInfo()
		}
		return nil
	}

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Match fb.Xver with the backend: 0 if the backend does not speak PROXY protocol, 1 or 2 only if the listener is configured for proxy_protocol
  2. On nginx: 'listen 8080 proxy_protocol;' and set_real_ip_from for the xray host
  3. Check backend logs for the reason it closed right after accept

Example fix

// json trojan fallback with PROXY v2 for a backend that supports it
"fallbacks": [{"dest": 8080, "xver": 2}]
# nginx:
# listen 127.0.0.1:8080 proxy_protocol;
# set_real_ip_from 127.0.0.1;
Defensive patterns

Strategy: validation

Validate before calling

// pick xver by backend capability, not by preference
func proxyProtoVer(backendSupportsProxyProto bool) int {
    if !backendSupportsProxyProto { return 0 }
    return 2 // v2 unless the backend only parses v1
}

Try / catch

if err := serverWriter.WriteMultiBuffer(buf.MultiBuffer{pro}); err != nil {
    // backend rejected/closed on PROXY header: mismatch between xver and listener
    return errors.New("failed to set PROXY protocol v", fb.Xver).Base(err).AtWarning()
}

Prevention

When it happens

Trigger: The fallback backend closed the connection immediately after accept (it does not understand PROXY protocol and resets), or the socket broke between the successful dial and this first write.

Common situations: xver is set to 1/2 but the backend (e.g. plain nginx http block without proxy_protocol listener) does not expect it, causing instant close; backend has proxy-protocol validation that rejects the header.

Related errors


AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15). Data as JSON: /api/errors/64d51878891a4ccb. Report an issue: GitHub.