I was looking for a way to redirect webbrowsers with SNI support from http to https AND webbrowsers without SNI support from https to http. The second case is important, for example, if the user get a https url sent by mail and opens that with an old browser.
I wanted to avoid working with user-agents, because I didn't want to keep track of new browser versions and update the config files, e.g. .htaccess, accordingly.
Therefore, I use javascript to detect SNI support and redirect the webbrowser. The following code checks whether the connection is SSL secured. If not SSL secured, it checks for SNI support and redirects the browser if SNI is supported:
<head>
<script type="text/javascript">
if (window.location.protocol != "https:") {
var img=document.createElement('img');
img.src='https://www.example.org/favicon.png';
img.onload = function() {
window.location.href = "https:" + window.location.href.substring(window.location.protocol.length);
}
img.style.display='none';
document.body.appendChild(img);
}
</script>
...
</head>
If the user visits the website for the first time and makes use of SSL encryption, he gets redirected to the http url in order to run the javascript code. Otherwise an old browser opens the secured website and gets an error message displayed.
RewriteCond %{HTTPS} =on
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?example\.org [NC]
RewriteRule .* http://%{HTTP_HOST}%{REQUEST_URI} [R=302,L]
Update 1: There is a redirect loop, because javascript doesn't deliver a referrer. This solution seems to avoid the loop:
<body>
<form style="display:none;" method="get" name="redirect"></form>
<script type="text/javascript">
if (window.location.protocol != "https:") {
var img=document.createElement('img');
img.src='https://www.example.org/favicon.png';
img.onload = function() {
document.redirect.action = "https:" + window.location.href.substring(window.location.protocol.length);
document.forms['redirect'].submit();
}
img.style.display='none';
document.body.appendChild(img);
}
</script
...
</body>
Update 2: We want to avoid redirecting the crawlers.
RewriteCond %{HTTPS} =on
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?example\.org [NC]
RewriteCond %{HTTP_USER_AGENT} !(googlebot|bingbot|baiduspider) [NC]
RewriteRule .* http://%{HTTP_HOST}%{REQUEST_URI} [R=302,L]
Update 3: We use the more reliable https detection via PHP (no redirection loop).
<body>
<?php if (empty($_SERVER["HTTPS"])) { ?>
<form style="display:none;" method="get" name="redirect">
<input type="hidden" name="redirected" value="true" />
</form>
<script type="text/javascript">
var img=document.createElement('img');
img.src='https://www.example.org/favicon.png';
img.onload = function() {
var redirect_form = document.forms["redirect"];
redirect_form.action = "https:" + window.location.href.substring(window.location.protocol.length);
redirect_form.submit();
}
img.style.display='none';
document.body.appendChild(img);
</script>
<?php } ?>
...
</body>
Open issues:
- How can I avoid the usage of user agents completely? If not possible, are there any important web crawler user agents missing?
- With the approach described in update 3, the back button doesn't work correctly. If the user get forwarded from http to https and then clicks on the back button, he gets redirected to http and then from http to https again. This issue can be solved with location.replace(), but the method doesn't support referrer.
- In the following example, how can I use location.replace() with http://www.example.org as referrer? Example: google.de -> http://www.example.org -> https://www.example.org -> back button click -> http://www.example.org (no redirection to google.de!!!) -> https://www.example.org
- If the browser doesn't support javascript, it is forced to use http.
- Are there any unknown problems with that approach?
Credits:
I decided to drop above htaccess rules due to the various problems. With the following code, web browsers that support Javascript and SNI get redirected to the SSL secured page without a check upon the user agent:
<body>
<?php if (empty($_SERVER["HTTPS"])) { ?>
<form style="display:none;" method="get" name="redirect"></form>
<script type="text/javascript">
var img=document.createElement('img');
img.src='https://www.example.org/favicon.png';
img.onload = function() {
var redirect_form = document.forms["redirect"];
redirect_form.action = "https:" + window.location.href.substring(window.location.protocol.length);
redirect_form.submit();
}
img.style.display='none';
document.body.appendChild(img);
</script>
<?php } ?>
...
</body>
These htaccess rules remove the trailing question mark created by above form:
RewriteCond %{HTTPS} =on
RewriteRule ^(.*)$ - [env=proto:https]
RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ - [env=proto:http]
RewriteCond %{THE_REQUEST} \?\ HTTP [NC]
RewriteRule .? %{ENV:proto}://%{HTTP_HOST}%{REQUEST_URI}? [R=301,L]
Old web browser that don't support SNI are redirected to http if they access the page over https (What is the most efficient code to detect and redirect SNI supported browsers?):
SetEnv SSL_TLS_SNI %{SSL:SSL_TLS_SNI}
RewriteCond %{HTTPS} =on
RewriteCond %{HTTP_USER_AGENT} MSIE\s6 [NC,OR]
RewriteCond %{HTTP_USER_AGENT} Windows\sNT\s5 [NC,OR]
RewriteCond %{HTTP_USER_AGENT} Android.*(Mobile)?\ [0-3] [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^(.*.symbian.*) [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^(.*.blackberry.*) [NC]
RewriteCond %{SSL:SSL_TLS_SNI} =""
RewriteRule .* http://%{HTTP_HOST}%{REQUEST_URI} [R=307,L]
来源:https://stackoverflow.com/questions/27892920/force-ssl-and-handle-browsers-without-sni-support