我可以分享我的经验和方法,将虚拟主机环境(每台服务器多个域)中的每个 IP 证书切换到所有域使用一个 IP 的负载平衡环境。
我们查看了我们的分析(每月有超过 100 万的独立访问者),其中大部分是希望在线购买汽车零部件的北美男性用户,并发现在 2014 年 3 月 8 日,大约 4% 的用户在使用 Windows XP 时使用Internet Explorer(其他次要——最坏的情况是 4.5% 的总用户会受到不支持 SNI 的影响)。请记住,我们无法“控制”这些用户,因此我们无法告诉他们切换浏览器。这个百分比也在迅速下降,至少在美国是这样。
我们首先认为非 SNI 客户与支持 SNI 的客户有一些不同的体验是“可以的”。
我们的方法是检测服务器端(使用 UA 字符串)哪些浏览器/操作系统组合不支持 SNI(正如其他人提到的:Wikipedia article on SNI support)。我们所有的域(约 120 个)都会有一条 A 记录指向一个负载均衡的 IP。我们为一个可以称为 generic-autoparts.com 的域提供了第二个 IP(也是负载平衡的)。
所以设置是[我没有与我使用的任何域相关联,如下例所示]:
mikesautoparts.com --> IP X 的名称服务器记录
dansautoparts.com --> IP X 的名称服务器记录
jensautoparts.com --> IP X 的名称服务器记录
...等
generic-autoparts.com --> IP Y 的域名服务器记录
如果客户点击http://www.dansautoparts.com 并支持 SNI,则不会发生任何事情。他浏览了 dansautoparts.com,到了结账的时候,他使用https://www.dansautoparts.com。
如果客户点击http://www.dansautoparts.com,并且我们检测到他不支持SNI,我们会立即将客户重定向到http://generic-autoparts.com/dansautoparts.com。他在那里购物,并在结帐时使用https://generic-autoparts.com/dansautoparts.com
现在,如果客户直接点击https://www.dansautoparts.com(电子邮件中的链接,搜索引擎中的索引页面),那么您就不走运了。他们会得到一个令人讨厌的证书错误。在我们的案例中,我们确保我们的系统发送的所有电子邮件都没有使用 https,并且我们知道搜索引擎没有索引我们的 https 页面。
每种环境都有不同的挑战和潜在的权衡取舍。我们发现这在我们的案例中效果很好,客户会“接受”(或不注意到)被重定向到 http://generic-autoparts.com/[ORIGINALDOMAIN].com 。我们还通过 generic-autoparts.com 确保结帐安全。
假设 20% 的非 SNI 用户注意到重定向,看起来很可疑,然后他们离开了。在我们的例子中,这是 0.8 - 0.9 %(基于 2014 年 3 月 8 日的数字)的用户,我们愿意“忍受”这一点。我现在没有这方面的具体数据,但整体销售保持稳定。 [编辑 2014 年 3 月 28 日:在我们转换 100% 的客户后,我们没有看到对销售的影响]
2014 年 7 月 8 日实施更新
事实证明,在服务器上静态检测每个 UA 代理字符串是不可能的。我们实现了以下 JavaScript 来检测浏览器的 SNI 功能。一般的方法是对需要 SNI 的域执行 JSONP 请求(Apache 通过“SSLStrictSNIVHostCheck on”支持这一点)。如果 JSONP 请求因超时而失败,我们会将客户重定向到非 SNI 域。
更复杂的是,我们不想仅仅因为 SNI_TEST_DOMAIN 关闭就重定向所有人。如果 JSONP 请求失败(由于无法直接检测 JSONP 失败而超时),我们会通过执行 HTTP“健康检查”请求来验证服务器是否可用。此外,我们不想在每次页面加载时都运行这个 javascript 代码,因为这会增加一些奇怪的超时和错误重定向许多客户的机会,所以我们在 SNI 检查完成后设置一个会话变量,这样就不会发生在客户浏览网站时再次访问。
我们知道,由于 JSONP 超时不可靠,我们会收到某些失败的错误检查,但自从实施此操作后,我们没有收到客户的任何投诉。
var redirect='http://REPLACE_WITH_NON_SNI_URL';
var sni_https_timeout, sni_http_timeout;
var https_req = $.ajax({
url : 'https://SNI_TEST_DOMAIN.com/snitest.php',
dataType : "jsonp",
}).done(function() {
window.clearTimeout(sni_https_timeout);
var request = $.ajax({
url: "index.php?ua=sni_check_done",
type: "POST"
});
})
sni_https_timeout = window.setTimeout(function() {
var http_req = $.ajax({
url : 'http://SNI_TEST_DOMAIN/sni_healthcheck.php',
dataType : "jsonp"
}).done(function()
{
window.clearTimeout(sni_http_timeout);
window.setTimeout(function()
{
window.location = redirect;
},
200);
});
sni_http_timeout = window.setTimeout(function() { sni_http_fail(); }, 8000);
}, 8000);
function sni_http_fail() {
var request = $.ajax({
url: "index.php?ua=sni_check_done",
type: "POST"
});
}
snitest.php / sni_healthcheck.php:
<?php
if (array_key_exists('callback', $_GET))
{
header( 'Content-type: application/javascript' );
echo "{$_GET['callback']}();\n";
}