If your website is behind a reverse proxy (Nginx, Apache2, HA Proxy, pound, squid, …), you will find that the built in Session.RemoteAddress (or request.RemoteAddress) will give you the IP address of your reverse proxy, and not the client making the request. This function will try to help with that.
This will NOT work if you are using TCP or NAT port forwards.
Var RetVal As String = Session.RemoteAddress
// IMPORTANT NOTE: This will only work where a reverse HTTP(S) proxy is being used. If your solution is based on:
// * A TCP proxy, there will be no header, and the RemoteAddress will ALWAYS point to the TCP proxy server.
// * Firewall port forwarding, there will also be NO header, but the RemoteAddress MAY point to a valid IP (or it may not).
// - - - - - - - - - - START LOOKING FOR REVERSE PROXY ADDRESS
// FrontEndProxies should be a comprehensive list of all reverse proxies.
// If an IP address is in this list, we need to look for another IP. If not in this list, we assume that it's a valid IP address.
// If we will only ever be accessed via a reverse proxy, we can safely ignore this first section.
// Note spaces at the beginning and the end. This is to help find a full match and not a partial match.
Var FrontEndProxies As String = " 127.0.0.1 x.x.x.x "
If FrontEndProxies.IndexOf(" " + RetVal + " ") = -1 Then Return RetVal // Return address if it's not in a list of possible frontend proxies
// - - - - - - - - - - STOP LOOKING FOR REVERSE PROXY ADDRESS
//NOTE: Since you presumably set up the reverse proxy yourself, or had someone do it for you, or are using an existing service,
// you should know which header is being used. This is here for you to find out in case you do not already know. Once you
// know what is being used, comment out the rest.
d("Session.RemoteAddress appears to be a reverse proxy. Looking for real IP...")
RetVal = Session.Header("X-Real-IP")
if RetVal <> "" Then
d("Using X-Real-IP")
Return RetVal
end if
RetVal = Session.Header("X-Forwarded-For").NthField(", ", 1)
if RetVal <> "" Then
d("Using X-Forwarded-For")
Return RetVal
end if
RetVal = Session.RemoteAddress
d("Using Session.RemoteAddress after all. Are you using a TCP proxy instead of a web proxy?")
Return RetVal
// Parts of this solution come from Tobias Bussman's code posted here: https://forum.xojo.com/t/reverse-proxy-ip-and-ssl/19096/3
One last note. If you are NOT using a reverse proxy, you really should consider doing so. While not required in some instances, in most instances a reverse proxy offers many benefits, including improved security and reduced server load.
Tim, I’m sorry but I have to disagree. The way the connection works is:
CLIENT BROWSER ==> REVERSE PROXY ==> XOJO WEB APP
More specifically,
CLIENT IP ==> REVERSE PROXY IP ==> WEB APP SERVER IP
The TCP connection to the XOJO WEB APP will always be coming from the REVERSE PROXY’s IP (unless the CLIENT somehow bypasses the proxy).
A properly configured reverse proxy will pass a header to the web app that contains the client’s IP address (usually in the X-Forwarded-For header), but there is no reasonable or safe way for the proxy to connect from the client’s IP address.
It’s possible that you mean that a properly configured reverse proxy will send a header that the Xojo web app recognises and inserts into the Session.RemoteAddress field, but my (certainly very limited) testing has shown that not to be the case thus far, and it’s not mentioned in the (sparse) documentation describing Session.RemoteAddress.
If you still feel I am mistaken, perhaps you could provide an explanation or link to documentation that would explain further what you mean.
Yes. I just configured nginx to pass a bogus value and the it came through (not on the instance above).
If you look at the feedback ticket from the thread that you linked, it’s marked as a duplicate of “23891 - Add support for reverse proxies to WE apps” which was marked “Implemented & Verified” in Xojo 2016r2.
I played around, but wasn’t able to get access to the internal address. You might be able to configure the proxy to pass it in your own custom header, but I did not look into that idea. WebApplication has a Port property, so if you’re configuring the reverse proxy yourself you could infer the internal address. I don’t have a better answer for this one because I’ve never needed it myself.
Linux requires installing a couple of libraries that aren’t included in every flavor / install. Have you installed those libraries? You’ll need libsoup and libunwind, both of which can be yum installed on CentOS 7.
I actually didn’t see the feedback ticket, I was looking for a solution to my (perceived) problem. Having dealt with this in various other programming languages I assumed what I was seeing was normal. Guess not
I have a couple of VPS’ I use for this. We have a new privacy law in South Africa that requires we keep unencrypted client data in the country. While I don’t use AWS or Digital Ocean, I was unhappy at having to move a few VPS’ away from Linode…
I’ll look at downloading the app when I get a spare moment next week.