Skip to content

proxy middleware drops upstream X-Forwarded-For chain on WebSocket Upgrade requests #2993

@kawaway

Description

@kawaway

Summary

The proxy middleware's WebSocket path only sets X-Forwarded-For when the header is absent, so whenever an upstream proxy has already populated the header, the middleware silently discards the entire incoming chain and never appends the proxy's own egress IP. The HTTP path does not have this problem because it delegates to net/http/httputil.ReverseProxy, which always appends RemoteAddr.

Affected code

middleware/proxy.go – inside ProxyConfig.ToMiddleware:

// current
if c.IsWebSocket() && req.Header.Get(echo.HeaderXForwardedFor) == "" {
    req.Header.Set(echo.HeaderXForwardedFor, c.RealIP())
}

When X-Forwarded-For is already set the condition is false, so neither the existing chain nor the proxy's own IP reaches the upstream.

Expected behaviour

Same semantics as the HTTP path: append c.RealIP() to any existing entries, preserving the full chain. This mirrors (*ProxyRequest).SetXForwarded in net/http/httputil.

Actual behaviour

If any upstream proxy has already set X-Forwarded-For, the middleware leaves the header untouched (the proxy's egress IP is never appended). Downstream services that rely on the "rightmost untrusted" rule — including echo's own ExtractIPFromXFFHeader — receive an incorrect or incomplete chain.

Steps to reproduce

  1. Place two reverse proxies in series: client → proxy-A (sets XFF) → Echo proxy middleware → backend.
  2. Connect via WebSocket.
  3. Inspect X-Forwarded-For at the backend.

Result: the chain contains only proxy-A's entry; proxy-B's egress IP is missing.
Expected: <client-ip>, <proxy-A-egress-ip> (i.e. the full chain with proxy-B appended).

Environment

  • Go: 1.24+ (reproduced on 1.21 as well)
  • Echo: latest main
  • Deployment: multi-hop WebSocket reverse proxy

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions