Thursday, September 19, 2013

HTTPS redirection with ELB and Varnish (infinite redirect loop)

Yesterday I was trying to deploy Varnish cache in front of my app server and was faced with the issue of infinite redirect loop for some pages. This problem happened when I tried to use HTTPS for pages which are not supposed to be.

The problem is like this:

I am terminating SSL at the ELB, behind that is the Varnish and behind that is the app server. Its a standard practice that whenever we hit the app server with HTTPS scheme for a page which is not supposed to be HTTPS it throws a 302 with the same page url , only difference being HTTP instead of HTTPS.

With SSL termination at ELB we can get the original scheme using a header which ELB sets called X-Forwarded-Proto . So my app was making use of this and doing a redirection from HTTPS -> HTTP. Everything was good.

The only problem was the Varnish cache caching 302. So this is what happened:

1) User hit https://example.com/myurl.
2) Varnish sends request to the backend.
3) Backend send a 302 redirect to http://example.com/myurl.
4) The response (302) gets cached in varnish

Now the problem is since scheme is not a part of the varnish hash key so effectively the mapping inside varnish is:

myurl : 302  irrespective of the scheme (https/http). So from now even if the user hit http://example.com/myurl he would always get a 302 to the same url. Infinite redirect loop.

Solution is very simple just make scheme part of the vcl_hash like this:

sub vcl_hash {
hash_data(req.url);
hash_data(req.http.X-Forwarded-Proto);
return(hash);
}


Here scheme (X-Forwarded-Proto) has been made a part of the key. So the internal mapping in varnish would like like:

myurl_http : Actual content
myurl:https: 302 redirect

Problem solved.

If you are facing infinite redirect loop for naked domain redirects (example.com -> www.example.com) or otherwise, read this.