This seems to hit everyone as they start out using SSL Termination and ASP.NET..
When you have a website running on a server with an SSL certificate installed, HttpRequestBase has a public property you can access as Request.IsSecureConnection that happily tells you if you’re running over SSL or not. This is useful when you need to generate full canonical URIs (for outbound links or return URIs) that include the scheme.
As your business matures and you scale out, maintaining SSL certificates across your IIS cluster becomes a headache, and many people opt to use SSL Termination – installing the SSL certificate only on a load balancer or reverse proxy. The reverse proxy then deals with the SSL encryption and decryption of traffic, forwarding the traffic, unencrypted, to the origin webserver.
So you take your code, that was generating URIs, and move to an SSL terminated environment and your code breaks. The URIs all get generated wrong, and you’re not sure what’s going on. You crack open the codebase on your local dev machine, with your self signed debug certificate and run a few tests – everything works. This is because on your local dev environment, you’re not SSL terminated, and on your staging and production environments, you are – you’re not going crazy, there isn’t any SSL here.
And you still need to generate valid URIs.
Supporting this scenario
There’s a non-standardised convention based header that a lot of terminating load balancers add to the HTTP headers of your request when they encrypt and decrypt your traffic - “X-Forwarded-Proto” which will be set to “https” if your load balancer has modified the scheme of your traffic. Hopefully your load balancer will add it, or support you adding it as a rule or configuration setting.
Request.IsSecureConnection is false in this scenario, and should always be false.- after all, the connection between your server and your load balancer is insecure, and Microsoft should resist the urge to change the meaning of this flag, so you’re going to want to support it another way.
So do the simplest thing that should possibly work, here are some extension methods:
using System; using System.Linq; using System.Web;public static class SslTerminationExtensions { public static bool IsSecureOrTerminatedSecureConnection(this HttpRequestBase request) { if (!request.IsSslTerminated()) { return request.IsSecureConnection; }
var header = request.Headers["X-Forwarded-Proto"]; return string.Equals(header, "https", StringComparison.OrdinalIgnoreCase); } public static bool IsSslTerminated(this HttpRequestBase request) { return request.Headers.AllKeys.Contains("X-Forwarded-Proto"); }
}
This will allow you to change any calls to Request.IsSecureConnection for calls to Request.IsSecureOrTerminatedSecureConnection() and your code will work correctly in both your self-signed dev environment, and your SSL terminated dev and QA environments.