假设我有一个网站
mywebsite.com
,使用 Apache Httpd 托管。现在我想要的是,每当任何用户输入 mywebsite.com
或 www.mywebsite.com
并且如果浏览器支持 SNI,那么它应该重定向到 https://www.mywebsite.com
,否则重定向到 http://www.mywebsite.com
。
那么,最有效的实现方法是什么?
下面的代码应该可以工作
Options -Indexes +FollowSymLinks
RewriteEngine on
RewriteCond %{HTTP_HOST} ^mywebsite.com$
RewriteCond %{HTTPS} (on|off)
RewriteRule ^(.*)$ http://www.mywebsite.com/$1 [R=302,L]
RewriteCond %{HTTPS} off
RewriteCond %{HTTP_USER_AGENT} !MSIE\s6
RewriteCond %{HTTP_USER_AGENT} !Windows\sNT\s5
RewriteCond %{HTTP_USER_AGENT} !^(.*.symbian.*) [NC]
RewriteCond %{HTTP_USER_AGENT} !^(.*.blackberry.*) [NC]
RewriteRule ^(.*)$ https://www.mywebsite.com/$1 [R=302,L]
这里我们忽略了大多数不支持 SNI 的浏览器,因此对于它们来说只会加载 http 版本。
更好的解决方案是
#Test if new browser and if so redirect to https
#new browser is not MSIE 5-8, not Android 0-3,
#not any symbian and not any blackbery
RewriteCond %{HTTPS} off
RewriteCond %{HTTP_USER_AGENT} !MSIE\ [5-8] [NC]
RewriteCond %{HTTP_USER_AGENT} !Android.*(Mobile)?\ [0-3] [NC]
RewriteCond %{HTTP_USER_AGENT} !^(.*.symbian.*) [NC]
RewriteCond %{HTTP_USER_AGENT} !^(.*.blackberry.*) [NC]
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
这会忽略 MSIE 5-8,其中不包括 XP 上的所有 IE,以及 VISTA 上可以使用的一些 IE。但允许XP用chrome、firefox、opera,所有这些都在XP上支持SNI。这至少允许XP用户使用https。同样,它假设所有 symbian、blackberry 都没有 sni。 Android 3 可以(我听说哪些平板电脑可以,手机需要 4)。
对于不同的解决方案,您可以有
#Could use this to set $_SERVER['SSL_TLS_SNI'] for php
SetEnv SSL_TLS_SNI %{SSL:SSL_TLS_SNI}
这会将 $_SERVER['SSL_TLS_SNI'] 设置为 %{SSL:SSL_TLS_SNI} (是的,可能是更好的代码)或域名。如果您知道 apache 返回的默认证书是什么并且有权访问该域,那么在其他域中,您可以让 php 对默认域执行测试 https,然后检查 $_SERVER['SSL_TLS_SNI'] 来测试 SNI前往 https。
请注意,如果非 sni 浏览器对需要 sni 的站点进行 https 访问,则无法避免出现错误消息。你能做的最好的就是
# Test if SNI will work and if not redirect to too old browser page
RewriteCond %{HTTPS} on
RewriteCond %{SSL:SSL_TLS_SNI} =""
RewriteRule ^ http://www.example.com/too-old-browser [L,R=307]
用户需要接受浏览器错误并继续访问网站,之后他会被重定向到 http 和错误页面。
这是我根据上面的答案使用的。
RewriteRule (.*) http://%{HTTP_HOST}%{REQUEST_URI} [R=302]
RewriteCond %{HTTP_USER_AGENT} !MSIE\s7
RewriteCond %{HTTP_USER_AGENT} !Windows\sNT\s5
RewriteCond %{HTTP_USER_AGENT} !Android.*(Mobile)?\ [0-3] [NC]
RewriteCond %{HTTP_USER_AGENT} !^(.*.symbian.*) [NC]
RewriteCond %{HTTP_USER_AGENT} !^(.*.blackberry.*) [NC]
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
RewriteCond %{HTTP_HOST} !^$
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteCond %{HTTPS}s ^on(s)|
RewriteRule ^ http%1://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
这将首先重写为 http,然后如果未列出 useragent,则重写为 https。 我不知道两次重写是否不好,但我确信谷歌和其他相关机器人更喜欢索引 https。这种方式可以实现这一点(我认为:)
我的做法倒退,但似乎有效。我确信您最好将浏览器列入黑名单并将其导航到 HTTP,而不是将浏览器列入白名单,因为要添加的浏览器太多了。
每个站点都不同,根据安全性,上述可能是一个选项。
请让我知道您的想法。
您可以设置第二个仅与 SNI 配合使用的服务器,并使第一个站点上的页面向其发出 Ajax 请求(如果需要,可能会使用一些标识符)。
如果客户端不支持 SNI,服务器将为所请求的主机名提供无效的证书。因此,该 Ajax 请求将不起作用。您可以让您的初始页面对该故障做出反应,以表明不支持 SNI。
当然,这并不完美,但这可能比必须依赖于用户代理的显式列表更好(取决于您所拥有的约束)。这更符合直接测试客户端功能的思路。
以下代码的灵感来自于user3281096的回答。
它尚未工作,必须考虑草案。
由于大多数浏览器现在都支持 HTTPS,因此我们的想法是避免双重重定向,并且仅当浏览器不支持 HTTPS 时才重定向到 HTTP。 为此,定义了一个名为 HTTP_ONLY 的环境变量,如果浏览器属于某个“黑”名单,则将其设置为 1 (= true)。
因为
<If>
和 <Else>
指令仅在 Apache 2.4 中可用,所以我不得不使用 <IfDefine>
来代替。
环境变量和
<IfDefine>
使用起来似乎很棘手,到目前为止我找不到让以下代码正常工作的方法。
测试是使用 Internet Explorer 11 执行的,方法是取消注释行
BrowserMatch "Trident/7" HTTP_ONLY=1
,人为将此浏览器插入到仅 HTTP 浏览器列表中。
如果您知道如何调整此代码以使其工作,请发表评论。
RewriteEngine On
RewriteBase /
SetEnv HTTP_ONLY 0
# Set a black list of (some) old browsers that do not support HTTPS
BrowserMatch "MSIE[5-8]" HTTP_ONLY=1
BrowserMatchNoCase "symbian" HTTP_ONLY=1
BrowserMatchNoCase "blackberry" HTTP_ONLY=1
# Activate next line for testing purposes with Internet Explorer 11
# like if it was Internet Explorer 5 to 8. Not sure that it works properly.
# BrowserMatch "Trident/7" HTTP_ONLY=1
# If the browser only supports HTTP, redirect to HTTP instead of HTTPS.
<IfDefine HTTP_ONLY>
RewriteCond %{ENV:HTTP_ONLY} true
RewriteRule ^(.*)$ http://www.somesite.ext/$1 [R=302,L]
</IfDefine>
# Redirect to HTTPS. Next two lines must be active on remote server.
<IfDefine HTTP_ONLY>
RewriteCond %{ENV:HTTP_ONLY} false
RewriteCond %{HTTPS} !on
RewriteRule ^(.*)$ https://www.somesite.ext/$1 [L,R=301]
</IfDefine>
# Force “www.somesite.ext” instead of just “somesite.ext”
#RewriteCond %{HTTP_HOST} .
#RewriteCond %{HTTP_HOST} !^www\.somesite\.ext [NC]
#RewriteRule (.*) https://www.somesite.ext/$1 [L,R=301]