Ran into this problem and referred it to MS Copilot which nailed it in 1:
-----------------------------------
In a B4J ABMaterial app, ws.UpgradeRequest.RemoteAddress reports the TCP peer address.
When running inside AWS EC2, this often shows the internal IP of the EC2 instance or
the load balancer, not the real client IP.
Reason:
- RemoteAddress only sees the immediate socket endpoint.
- In AWS, traffic is routed through a load balancer or NAT gateway, so the peer is
the EC2 private IP or proxy.
- The true client IP is carried in the X-Forwarded-For HTTP header.
Solution:
- Read the X-Forwarded-For header during the WebSocket upgrade request:
Dim clientIP As String = ws.UpgradeRequest.GetHeader("X-Forwarded-For")
If clientIP = "" Then
clientIP = ws.UpgradeRequest.RemoteAddress
End If
Log("Client IP: " & clientIP)
Notes:
- X-Forwarded-For may contain multiple IPs (comma-separated). The first one is usually
the real client.
- Ensure your AWS ALB/ELB or reverse proxy (NGINX/Apache) is configured to forward
X-Forwarded-For.
- For WebSockets, capture the header during the initial HTTP handshake and store it,
since headers are not resent after upgrade.
- Only trust X-Forwarded-For if all traffic comes through your AWS load balancer,
otherwise it can be spoofed.
-----------------------------------
I have tested this and it works:
-----------------------------------
In a B4J ABMaterial app, ws.UpgradeRequest.RemoteAddress reports the TCP peer address.
When running inside AWS EC2, this often shows the internal IP of the EC2 instance or
the load balancer, not the real client IP.
Reason:
- RemoteAddress only sees the immediate socket endpoint.
- In AWS, traffic is routed through a load balancer or NAT gateway, so the peer is
the EC2 private IP or proxy.
- The true client IP is carried in the X-Forwarded-For HTTP header.
Solution:
- Read the X-Forwarded-For header during the WebSocket upgrade request:
Dim clientIP As String = ws.UpgradeRequest.GetHeader("X-Forwarded-For")
If clientIP = "" Then
clientIP = ws.UpgradeRequest.RemoteAddress
End If
Log("Client IP: " & clientIP)
Notes:
- X-Forwarded-For may contain multiple IPs (comma-separated). The first one is usually
the real client.
- Ensure your AWS ALB/ELB or reverse proxy (NGINX/Apache) is configured to forward
X-Forwarded-For.
- For WebSockets, capture the header during the initial HTTP handshake and store it,
since headers are not resent after upgrade.
- Only trust X-Forwarded-For if all traffic comes through your AWS load balancer,
otherwise it can be spoofed.
-----------------------------------
I have tested this and it works:
B4X:
'First try X-Forwarded-For header (used by AWS ALB/ELB, NGINX, Apache)
Private wrk_ip_addr As String = ws.UpgradeRequest.GetHeader("X-Forwarded-For")
If wrk_ip_addr <> "" Then
'X-Forwarded-For may contain multiple IPs: client, proxy, load balancer
'first one is usually real client
Private wrk_ip_addr_parts() As String = Regex.Split(",", wrk_ip_addr)
wrk_ip_addr = wrk_ip_addr_parts(0).Trim
Else
'Fallback to raw socket address
'ws.UpgradeRequest.RemoteAddress, see:
'https://www.b4x.com/android/forum/threads/how-to-obtain-wan-ip-public-ip-of-the-websocket-wep-app-user.91611/
wrk_ip_addr = ws.UpgradeRequest.RemoteAddress
End If
Last edited: