Magento 2 Correcting User IP Detection For Sites Using a CDN Like Cloudflare
By: Carlos Cerda
I was working on a feature the other day that uses the user’s IP address to determine if the user is allowed to access the Magento 2 admin interface. I was able to find an extension for this functionality so I decided to go with that.
The functionality was working perfectly for me while I tested locally so I decided to install this extension on the production site that uses the Content Distribution Network (CDN) called Cloudflare. But weirdly enough, I was not able to access the Magento 2 admin interface on the production site once I added the extension, even though I correctly whitelisted my IP address on the site to be allowed to access the admin interface.
I began to look into the reasons for this and I noticed something weird about the IP address being logged by the server’s access log when I visited the site. The IP address being logged was not the IP address that belonged to me. It was an IP address from one of Cloudflare’s servers!
After doing some research, it turns out that this is expected behavior. The way that Cloudflare works, like other CDNs, is it receives the initial request being made by the user’s browser to a site, like a middle man, and then sends another request to the site’s server directly if it needs to. The diagram below shows the difference between using a CDN and not using one, but if you’re curious to learn more about the philosophy of a CDN, here is a good resource describing how a CDN works.
Web servers, like Apache, use the concept of a “remote address” which is supposed to represent the IP address of the client/browser that made the request to the web server. In the normal scenario, there are only two players in this workflow, the client/browser and the server. The value of the “remote address” in the web server is in fact the user’s IP address.
When using a CDN, there is now a middle man that intercepts the browser’s call to the server. The CDN then sends its own request to that server in place of the browsers call. The web server doesn’t know what or how requests are created, they just listen for incoming requests and do work based on those requests, so when the CDN sends the request to the server, the server receives the remote address value of one of the CDN’s servers.
Being aware of this issue, CDNs send additional headers containing useful information about the original user’s request.
CDNs Provide Useful Additional Headers
Most CDNs return the HTTP_X_FORWARDED_FOR request header, which contains a string delimited list of IP addresses that have been involved in the request transaction starting from the customers original request and including the request sent by the CDN itself. The first value in this string is typically the user’s IP address. Cloudflare further assists us by sending a HTTP_CF_CONNECTING_IP request header which isolates the user’s IP address.
The Magento Fix
Going back to my Magento related problem, the extension I was using correctly made use of the following Magento helper class:
You might be thinking, why do I need a class if I could just access the value of the remote address from the php request object directly like this?
The RemoteAddress Class
After looking into this RemoteAddress class, it became clear to me why this class exists and reminded me why helper classes in general exist.
Using dependency injection and the di.xml file, you are allowed to specify alternative http request headers to be used in place of the “remote_address” value when returning a value from the RemoteAddress class. This is very useful to us because we can tell Magento to look in headers mentioned before that would contain the IP address of the user browsing the site. Without changing any real functionality, we were able to solve this problem!
I’ve gone the extra step and wrapped the solution in a Magento 2 extension, so all you need to do is install it and you should be all set.
To summarize the article, a developer working with a Magento website, and often times any other framework, that has a component that depends on IP address detection needs to consider that the CDN wont return the user’s IP address in the “remote_address” request header. The CDN will typically return that value in another request header, so as a developer you need to remember to look in the right place, or your component wont work as expected. In the case of Magento 2, thanks to dependency injection, we can add argument to a di.xml file and tell Magento what request headers to use when retrieving the “remote_address” value.