客户端程序访问 HTTPS 域名时,提示证书不匹配。
原因:客户端不支持 SNI
当域名经过CDN,或者一个IP上有多个不同证书的 HTTPS 网站时,如果客户端程序不支持 SNI,就会提示证书不匹配的现象。
排查步骤:
- Windows 用 IE、Firefox、Chrome 等浏览器打开该 https 网站,显示证书正常。
- Linux 用
curl -v https://domain.com | head -1
命令,显示证书正常。
以上任意一个步骤均可以验证服务器证书部署是否有问题。如果服务器证书没有问题,则问题出在客户端不支持 SNI。
解决办法:
- 临时不校验证书,等后续客户端的代码升级之后,再进行校验证书。
- 升级 http client 客户端的版本库到最新的稳定版本,同时修改相应的配置。
- 域名解析不经过CDN,或是该域名在服务器上作为第一个或是唯一的SSL证书部署。[临时回避触发问题的条件,治标不治本]
浏览器访问HTTPS 网站时,静态资源显示有问题, 提示 mix-content
原因:访问流量经过了一个或多个负载均衡,后端代码以为用户访问的还是 HTTP 协议,拼接静态资源的URL仍然以 "http://" 开头。
解决办法: 服务器需要额外配置,某些后端使用旧框架代码需要微调。
nginx 配置
location / {
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_ignore_client_abort on;
proxy_pass http://upstream_srv;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
}
tomcat 配置
server.xml 文件中,<Host> 节点下面 或是 <Engine> 节点下面 增加这一行:
<Valve className="org.apache.catalina.valves.RemoteIpValve" remoteIpHeader="X-Forwarded-For" remoteIpProxiesHeader="x-forwarded-by" protocolHeader="X-Forwarded-Proto" protocolHeaderHttpsValue="https"/>
后端代码框架处理:
判断 HTTP 头 X-Forwarded-Proto 为 https 或 HTTPS 头为 on,两种情况存在任意一个,
则认为此服务是 https 协议。
# PHP 代码判断协议 schema
# _SERVER['HTTPS'] == 'on' 或 _SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' 时,为 https 协议
$scheme = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) ? 'https' : 'http';
前端代码处理
- 佛系前端1
前端代码不判断协议类型,绝对路径不写http://domain.com/j.js
, 也不写https://domain.com/j.js
, 直接写成自适应的//domain.com/j.js
, 谷歌,推特都是这样做的。 - 佛系前端2
当然 url中不写域名,写成相对路径的/path/file.js
或是../path/file.js
也是没有问题的。
强制 nginx 跳转到 https
set $flag 0;
if ($scheme = "http") { set $flag "1${flag}"; }
if ($scheme = "https") { set $flag "0${flag}"; }
if ($http_x_forwarded_proto = "https" ) { set $flag "0${flag}"; }
if ($flag ~ "^1") { rewrite ^(.*)$ https://$host/$1 permanent;}
# HSTS
add_header Strict-Transport-Security "max-age=120; preload";
HTTP/2 协议 不支持 WebSocket
截至到目前,2017年11月,HTTP/2 协议 不支持 WebSocket ,如果有业务需要使用WebSocket 协议,记得要关闭 HTTP/2 。
如果有业务依赖https,又需要推送消息,可考虑 SSE(使用 polyfills 解决 IE/EDGE 的支持问题,如 https://github.com/Yaffle/EventSource)