Search code examples
.htaccesshttp-redirecthttps

https redirect for Network Solutions


I am using Network Solutions as a hosting provider with a UNIX hosting package. I need to redirect certain pages to https (login, etc). After going through a lot of testing, I ended up with the following .htaccess entry:

RewriteEngine on

RewriteCond %{SERVER_PORT} !^443$ [OR]
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://[domain_redacted]/$1 [R=301,L]

This resulted in an infinite redirect loop. After discussions with Network Solutions, they told me I needed to 301 redirect (apparently what I was doing is not a 301 redirect ...), so I then tried the following in .htaccess:

Redirect 301 / https://[domain_redacted]/

This, of course, resulted in an infinite loop, as one would expect. Since none of these methods worked, I put the following php script on my server to test https detection through PHP:

<?php 
if(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') { 
    echo 'HTTPS IS ON';
} else {
    echo 'HTTPS IS OFF';
} 
?>

When requesting this page over both HTTP and HTTPS, the script returns OFF.

So, what other methods exist to detect HTTPS server side in Apache or PHP?


Solution

  • I asked only so I could answer the question, mostly because I couldn't find a solution anywhere.

    Background: The way Network Solutions handles their shared hosting systems, you don't actually connect to a server, you connect to a proxy server (this makes sense). When you perform an https redirect like:

    RewriteEngine on
    
    RewriteCond %{SERVER_PORT} !^443$ [OR]
    RewriteCond %{HTTPS} off
    RewriteRule ^(.*)$ https://[domain_redacted]/$1 [R=301,L]
    

    You do, in fact, redirect the page to HTTPS. However, you are performing your HTTPS transaction with the proxy server, not your hosting server. The proxy server translates your HTTPS connection with it to HTTP over port 80. When attempting to detect this with .htaccess, the server doesn't see HTTPS because the connection between your hosting server and the proxy will always be HTTP on port 80. Because PHP pulls the HTTPS variable from Apache, it will have the identical outcome to detecting HTTP through .htaccess.

    Therefore, unless I am mistaken, it is impossible to perform a self-referencing HTTP to HTTPS redirect when on shared hosting from Network Solutions. The request must somehow change (either through a change in the URI or the Query String) in order for the server to detect the change. This, of course, is completely spoofable (e.g. no matter how you change the URI/Query String, one can simply type it into the browser as http and the server would never know), negating the whole point of HTTPS.

    I tried checking if %{HTTP_REFERER} referenced itself, but it did not using mod_rewrite.

    The only workaround I can come up with is through client-side detection with javascript. This has its own pitfalls (NoScript, etc). But I cannot come up with another solution to the problem. The following code will approximate a 301 redirect.

    if(document.URL.match(/^https/i) == null) {
        var NetworkSolutionsSucks = document.createElement('meta');
        with (NetworkSolutionsSucks) {
            httpEquiv = 'refresh';
            content = '0;url=' + document.URL.replace(/^http/i,'https');
        }
        document.head.appendChild(NetworkSolutionsSucks);
    
        var msg = document.createElement('div');
        with (msg) {
            innerHTML = 'This page requires a secure connection, you are being redirected.<br><br>';
            innerHTML += 'If the page does not load, please click <a href="' + document.URL.replace(/^http/i,'https') + '">Here</a>';
        }
        var newBody = document.createElement('body');
    
        newBody.appendChild(msg);
        document.replaceChild(newBody, document.body);
    }
    

    Include the above script in the header of any document you want to force HTTPS (forcing HTTP is left as an exercise for the reader - it's a simpler script, not requiring the manual redirect on some failure). You should use a noscript tag with this (obviously); you can use the following:

    <noscript id="networkSolutionsHTTPSRedirect">This site requires javascript in order to operate properly. Please enable javascript and refresh this page.</noscript>
    <style type="text/css">#networkSolutionsHTTPSRedirect{position: fixed; height: 100%; width: 100%; top: 0; left: 0; z-index: 500;}</style>
    <script type="text/javascript">
    <!--
    document.getElementById("networkSolutionsHTTPSRedirect").style.visibility = "hidden";
    document.getElementById("networkSolutionsHTTPSRedirect").style.display = "none";
    -->
    </script>
    

    EDIT: I figured out a way to do this server side:

    You can try to force the entire site to HTTPS if you do the following on every page:

    <?php
    if((!isset($_ENV['HTTP_REFERER']) || preg_match('/^http[s]{0,1}:\/\/' . $_ENV['SERVER_NAME'] . '/i',$_ENV['HTTP_REFERER']) == 0) && !isset($_ENV[HTTP_X_HTTPS_REDIRECT])) {
        header('X-HTTPS-REDIRECT: true\n');
        header('Location:' . preg_replace('/^http/','https', $_ENV['SCRIPT_URI']));
    }
    ?>
    

    This should introduce a redirect to all requests that don't have HTTP_REFERER set to your site and do not carry the custom X-HTTPS-REDIRECT header that you inject. This doesn't really detect HTTPS, but performs a reasonable facsimile (at least its server side).

    You could, of course, modify the regex so that only certain sites need to be HTTPS by matching the REFERER to the portion of your domain that you want secured and then negate the test to force HTTP.