HTTPS Redirect – Fix Redirect Loop When Forcing HTTPS

.htaccessamazon-elbhttpsssl

I am using the following to force HTTPS on my main website.

### Force SSL
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

If you want to view the full htaccess file, I have placed it below:

<IfModule mod_rewrite.c>

    RewriteEngine On
    RewriteBase /

    ### Blacklist via Referrers

    RewriteCond %{HTTP_REFERER} removed\.net [NC,OR]
    RewriteCond %{HTTP_REFERER} removed\.com [NC,OR]
    RewriteCond %{HTTP_REFERER} removed\.removed\.com [NC,OR]
    RewriteCond %{HTTP_REFERER} removed\.com [NC,OR]
    RewriteCond %{HTTP_REFERER} removed\.net [NC,OR]
    RewriteCond %{HTTP_REFERER} removed\.sx [NC,OR]
    RewriteCond %{HTTP_REFERER} removed\.com [NC,OR]
    RewriteCond %{HTTP_REFERER} removed\.com [NC,OR]
    RewriteCond %{HTTP_REFERER} removed\.com [NC,OR]
    RewriteCond %{HTTP_REFERER} removed\.com [NC,OR]
    RewriteCond %{HTTP_REFERER} removed\.org [NC]
    RewriteRule ^(.*)$ - [F,L]

    ### Force SSL
    #RewriteCond %{HTTPS} !=on
    #RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

    ### Canonicalize codeigniter URLs

    # If your default controller is something other than
    # "welcome" you should probably change this
    RewriteRule ^(welcome(/index)?|index(\.php)?)/?$ / [L,R=301]
    RewriteRule ^(.*)/index/?$ $1 [L,R=301]

    # Removes trailing slashes (prevents SEO duplicate content issues)
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.+)/$ $1 [L,R=301]

    # Enforce NO www
    RewriteCond %{HTTP_HOST} ^www [NC]
    RewriteRule ^(.*)$ https://removed.com/$1 [L,R=301]

    # Removes access to the system folder by users.
    # Additionally this will allow you to create a System.php controller,
    # previously this would not have been possible.
    # 'system' can be replaced if you have renamed your system folder.
    RewriteCond %{REQUEST_URI} ^system.*
    RewriteRule ^(.*)$ /index.php/$1 [L]

    # Checks to see if the user is attempting to access a valid file,
    # such as an image or css document, if this isn't true it sends the
    # request to index.php
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)$ index.php/$1 [L]

</IfModule>

<IfModule !mod_rewrite.c>

    # Without mod_rewrite, route 404's to the front controller
    ErrorDocument 404 /index.php

</IfModule>

I am struggling to figure how the redirect loop is happening.

Update

I did not make it clear that my web server (Apache) is only configured with one virtual host on port 80 that gets HTTP and HTTPS traffic directed to it by the load balancer. The load balancer handles the SSL connection.

Best Answer

Thanks to HBruijn's comment, I understood why I was getting the redirect loop.

The traffic from the ELB will always be HTTP as it handles the HTTPS traffic to the user but to the server it's HTTP, so my previous rule will result in a loop.

After some research I found that the load balancer forwards a few userful headers like X-Forwarded-Proto. This can be used to determine if the client is using HTTP or HTTPS like so:

### Force HTTPS
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

I hope the above helps someone else when trying to force HTTPS behind Amazon Web Services Load balancer.