What is a Reverse Proxy
During lead developer roles of previous ASP.NET projects, I’ve needed to deploy into some fairly heavy enterprise infrastructure - once needing to deal with some unexpected behaviour caused by reverse proxy’s. I’ll describe in this article some things to keep in mind when doing this to ensure everything still runs smoothly and correctly all the way from the development environment to production.
A reverse proxy has a few functions, one function is acting as the network load balancer as I talked about in my previous post Web Farm Considerations. It can also be the point that processes SSL encryption.
Many people might already familiar a standard “proxy” that they may have needed to configure their browser to use to make connections to the internet (typically from inside a company network). More specifically this is known as a “forward proxy” and is to do with outgoing traffic, i.e. instead of the browser making a connection directly to a website, it will send the request first via a proxy server somewhere in the company’s network. The proxy server may be the only machine on the company network that can access the internet and gives the company further control over blocking of certain sites, monitor outgoing requests, virus scanning, caching etc.
A reverse proxy on the other hand sits on the server side – and is essentially the opposite of a forward proxy.
Application Path Issues
The scenario described here is when your ASP.NET application (ApplicationX) is to be deployed to a subdirectory of an already existent company wide domain (www.company.com), i.e to http://www.company.com/ApplicationX.
The user’s browser will connect to the reverse proxy which has a public address such as https://www.company.com/ApplicationX (so essentially connecting to it without realising it as opposed to the forward proxy that needs to be configured) – which will then forward the request to a server internal to that network such as http://internal05/ApplicationX to do the real processing of the request and dishing up the web content to the user. Acting as a NLB (Network Load Balancer) the request could also be redirected to http://internal0[1-4]/ApplicationX unbeknown to the user. These internal0[1-5] servers are only accessible within the company network, behind the firewall and not accessible directly from the cloud/internet.
Now – here is the issue to keep in mind when deploying in this type of scenario – it’s quite possible for servers internal01-05.company.com to be servers dedicated to ApplicationX – in this case it would seem natural to simply set the default website on these to serve ApplicationX i.e directly at http://internal05.company.com.
The problem with this is ASP.NET will send virtual addresses such as WebResources.axd (some more info about WebResource.axd in my post Securing Static (Non-ASP.Net) Files) back to the client browser as the absolute address “/WebResource.axd”. This will cause the client browser to make requests to https://www.company.com/WebResource.axd – not good ! Instead it should be https://www.company.com/ApplicationX/WebResource.axd .
To achieve this, the option is to create a virtual directory on the internal server called ApplicationX so that it is served from http://internal05.company.com/ApplicationX (matching the reverse proxy’s URL structure). ASP.NET will now return the absolute address “/ApplicationX/WebResource.axd” back to the client browser which will resolve correctly behind the reverse proxy.
In terms of linking to resources yourself, ensure that you don’t use something like Request.Url. This will work in your development environment, but when deployed behind the reverse proxy – Request.Url will actually return http://internal05.company.com/ApplicationX (as far as the ASP.NET application is concerned, it is serving content from this location as the reverse proxy has made a request to the internal server with this URL). Obviously from the client browser however any references to this address will not work since this server is behind the company firewall.
Instead, make use of the special character “~” wherever possible (in conjuction with ResolveUrl where necessary) which will resolve to “/ApplicationX”. Here are some examples (note that server side controls “~” can be specified and interpreted directly within the properties themselves without use of ResolveUrl):
<asp:Image runat="server" ID="image" ImageUrl="~/Penguins.jpg" />
<img alt="Penguins" src='<%=ResolveUrl("~/Penguins.jpg") %>' />
This will mean the client browser will request items back to http://www.company.com/ApplicationX as well as ensure everything still works in the development environment.
Using Request.ApplicationPath instead of “~” will probably cause issues if you are running your Visual Studio web server development environment from the root directory. Linking to resources from the root directory - Request.Application path returns “/” and you end up with with a request to //Penguins.jpg, which will fail, such as below:
<img alt="Penguins" src='<%=Request.ApplicationPath + "/Penguins.jpg" %>' />
The solution is to either use “~” or alternatively run your development environment from a virtual path instead of the root directory if you do prefer using Request.ApplicationPath.
In summary, before deploying to a more complicated infrastructure than a simple single server scenario, it pays to think through any potentially surprising behaviour you might see. To save potential delays further down the track it can be wise to put the time aside to setup a test environment first that includes the reverse proxy, load balancing servers etc to ensure everything still works as expected.
Good luck! Hamish