I’ve had a fun & games sort of day working through this Magento SSL offload on Amazon’s ELB and I thought it’d be worth documenting what’s involved, in the process I’ll give some steps to get it working and explain what the new (since version 1.6.2) Offloader header
config option is for and how it helps.
Background
Firstly, a bit of background – if you just want the quick fix, skip right to it. The Internet can beam magically onto your computer screen one of two ways: http
and https
. http
means the things you see everyone else can see too, the content is unencrypted and sent from the server to your computer. If you’re at a wifi hotspot, what you do online is being broadcast to everyone around you. Conversely, an https
connection means the content is securely encrypted, and the endpoint server you are connecting to, is probably who they say they are. So we want https in public places, or when financial or personal information is involved – and if we’re paranoid, we want it all the time.
But, the SSL encryption has to be processed by the server, and that takes server cycles (albeit not many). One technique to reduce the work your Magento server is doing is to offload the SSL processing to a server in front of your Magento server(s) – this is called SSL offloading. SSL Offloading can be handled by a large number of proxy servers (Apache and Nginx to name two) – we run our stores on Amazon’s EC2 and have access to the SSL offload capability of their ELB load balancer, which is what I will walk through here.
I drew a diagram on paper for you.
What you can see is the secure connection arrives on port 443 to the ELB load balancer, and then on the Amazon internal network, the connection becomes plain http on port 80 and is passed to the Magento cluster for processing. This means the Magento servers only do the work as if a plain http connection had been used.
The problem
There’s a problem with this though. Magento (or indeed any application in a similar arrangement) will think the connection is not secure right through to the user. This means if Magento has been configured to force secure URL’s (in say the admin, or frontend) it will redirect the user to the https: portion of the site, but it is already on it – and so we get an infinite redirect loop. The flipside, if Magento is not configured to force SSL, is that it will write url’s to the non-secure base url (http) because it thinks the original request is on http. This will result in most browsers warning the user about mixed SSL and non SSL-content – which can scare users away. So neither scenario is really very desirable.
The solution has actually existed for ages, the SSL Offloader can pass a message to the destination server by way of an additional HTTP header – the application can interpret it and will then know the connection is secure, great! Magento supports this check in two ways. Initially it just checks for a header of HTTPS
that’s not ‘off’. For non-standard setups, as of version 1.6.2 you can now specify you own header which if populated, will be interpreted by Magento to mean the connection is secure regardless of which protocol it arrived on.
This is the relevant code:
public function isCurrentlySecure() { $standardRule = !empty($_SERVER['HTTPS']) && ('off' != $_SERVER['HTTPS']); $offloaderHeader = trim((string) Mage::getConfig()->getNode(self::XML_PATH_OFFLOADER_HEADER, 'default')); if ((!empty($offloaderHeader) && !empty($_SERVER[$offloaderHeader])) || $standardRule) { return true; } // ... } |
The problem is that Amazon’s AWS doesn’t send a header with the semantics of if it’s present, it’s secure which is what Magento is coded to assume. Instead, ELB sends two headers, one telling you what protocol, and another telling you what port.
How to fix it?
We could modify Magento so that it can handle different header semantics, but modifying Magento should be your absolute last resort, trust me. We can achieve a much simpler victory by just adding a simple SetEnvIf
Apache directive (assuming you use Apache) to check for the header that Amazon’s ELB uses, and set the header that Magento is expecting.
# Detect the ELB header and set the header magento expects SetEnvIf X-Forwarded-Proto https HTTPS=on |
With that in either your Apache server config your your .htaccess
file, you should be good to go.
Note: In this article I haven’t covered actually getting the SSL, and setting it up for AWS. It can all be done through the web management console now, this is a good overview from StackOverflow to get you started with the openssl commands, etc. If there’s enough interest, I’ll do a full walk through post – let me know.
Awesome post Ashley. Will save this one for leter use. =]
Spot on mate, been trying to work this out for weeks!
Cheers
I’m currently setting up our magento website in Amazon and after setting up SSL on Amazon ELB and when using a simple html file, I can see that SSL is working (since the padlock icon is seen in firefox browser). But when I check the Magento website’s checkout page I’m not seeing the padlock icon, check this out:
https://www.leluv.xxx/amazon-check.html
vs:
https://www.leluv.xxx/checkout/cart/
So far I already added the below line to .htaccess file, is there anything else I need to do to make this works? THANKS IN ADVANCE..
SetEnvIf X-Forwarded-Proto https HTTPS=on
Looks fine to me, Magento is emitting the https URL’s that it should.
You just need to fix up the absolute URL’s you’re using on the footer, e.g:
http://www.leluv.xxx/skin/frontend/default/default/images/credit-card-new.png
View the page source, or inspect it with Chrome inspector or Firebug to see which resources are being loaded without https.
These url’s are probably hard coded in your theme/template files. Fire your Magento developer and get a good one 😛
SetEnvIf X-Forwarded-Proto https HTTPS=on can’t work since you can’t use an environnement variable in the condition statement.$*
Read Apache doc here : http://httpd.apache.org/docs/2.2/mod/mod_setenvif.html
Weird…working fine for me. Maybe depends on Apache version? In any case, best to test this in dev before you release it (that goes without saying, right?)
Hello Ashley,
thanks for the information and description. I’ve identified another issue which I describe here in my blogpost as Step 2. Might help some users. Step 3 is the same as yours.
http://blog.ideaday.de/max/2012/12/magento-https-redirect-loop-ssl-offloading-proxies-pound-nginx/
Have a nice day!
Max
Great post! Thank you!!!
For NGINX, I used the following:
Within the server block:
# Check if Load Balancer handled SSL
set $my_https “off”;
if ($http_x_forwarded_proto = “https”) {
set $my_https “on”;
}
I then added with the rest of the fastcgi params:
fastcgi_param HTTPS $my_https;
index.php
above Mage::run($mageRunCode, $mageRunType);
try the following:
if (isset($_SERVER[‘HTTP_X_FORWARDED_PROTO’]) && $_SERVER[‘HTTP_X_FORWARDED_PROTO’] == ‘https’) {
$_SERVER[‘HTTPS’] = ‘on’;
$_SERVER[‘SERVER_PORT’] = 443;
}