我问只是为了回答这个问题,主要是因为我在任何地方都找不到解决方案。
背景:
Network Solutions 处理其共享托管系统的方式,您实际上并没有连接到服务器,而是连接到代理服务器(这是有道理的)。当您执行 https 重定向时:
RewriteEngine on
RewriteCond %{SERVER_PORT} !^443$ [OR]
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://[domain_redacted]/$1 [R=301,L]
实际上,您确实会将页面重定向到 HTTPS。但是,您正在与代理服务器而不是托管服务器执行 HTTPS 事务。代理服务器将您的 HTTPS 连接转换为端口 80 上的 HTTP。当尝试使用 .htaccess 检测时,服务器看不到 HTTPS,因为您的托管服务器和代理之间的连接将始终是端口 80 上的 HTTP。因为PHP 从 Apache 中提取 HTTPS 变量,它与通过 .htaccess 检测 HTTP 的结果相同。
因此,除非我弄错了,否则在网络解决方案的共享主机上执行自引用 HTTP 到 HTTPS 重定向是不可能的。请求必须以某种方式更改(通过 URI 或查询字符串的更改),以便服务器检测到更改。当然,这完全是可欺骗的(例如,无论您如何更改 URI/查询字符串,都可以简单地将其作为 http 输入到浏览器中,而服务器永远不会知道),否定了 HTTPS 的全部意义。
我尝试检查 %{HTTP_REFERER} 是否引用了自己,但它没有使用 mod_rewrite。
我能想出的唯一解决方法是使用 javascript 进行客户端检测。这有其自身的缺陷(NoScript 等)。但我无法想出另一个解决问题的方法。以下代码将近似于 301 重定向。
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);
}
在您想要强制 HTTPS 的任何文档的标题中包含上述脚本(强制 HTTP 留给读者作为练习 - 这是一个更简单的脚本,在某些失败时不需要手动重定向)。你应该使用 noscript 标签(显然);您可以使用以下内容:
<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>
编辑:我想出了一种方法来做这个服务器端:
如果您在每个页面上执行以下操作,您可以尝试强制整个站点使用 HTTPS:
<?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']));
}
?>
这应该会为所有未将 HTTP_REFERER 设置到您的站点且不携带您注入的自定义 X-HTTPS-REDIRECT 标头的请求引入重定向。这并不能真正检测到 HTTPS,但会执行合理的传真(至少在其服务器端)。
当然,您可以修改正则表达式,以便通过将 REFERER 与您想要保护的域部分进行匹配,然后取消测试以强制使用 HTTP,从而只有某些网站需要 HTTPS。