nginx的access日志打印十六进制\x16\x03\x01\x02\x00\x01
背景
心血来潮想给自己的小网站加上https协议,照着网上一顿操作,结果浏览器提示“ERR_SSL_PROTOCOL_ERROR”无法打开。查看nginx的error日志没有报错,查看access日志如下,其中$request字段打印的全是十六进制(以\x16\x03\x01\x02\x00\x01开头)。
先说解决办法-启用SSL支持
不同版本(nginx -v查看)的Nginx启用ssl的配置不一样!!
#版本1.15.0及以下 listen 443; ssl on; #版本1.15.0以上 listen 443 ssl;
--------- 分割线 ---------
再看看这一串十六进制怎么回事
有几个疑问需要解决一下:
1.为什么浏览器中访问一次,access日志会有四条记录?
本机fiddler抓包发现,浏览器中访问一次,chrome向服务器发出了四次https连接请求。
2.十六进制是乱码吗?
.尝试解码“\x16\x03...”这一段,失败不行!其实熟悉ASCII码就知道这串是转换不成字符串的,可见字符是从32(空格)开始的
.怀疑是中文,照着网上的方法将nginx的logformat增加一个escape属性“log_format main escape=json”,结果打印的十六进制部分变成了“\u0016\u0003\u0001\u0002”,失败!
所以这些十六进制并不是乱码!怎么肥事?
首先查看nginx的logformat可以知道显示成十六进制的这部分是$request,$request由三个部分组成:http请求方法 http的url http的版本,如“GET / HTTP/1.1.0”。HTTP报文中请求方法与URL直接用空格(\x20)分隔,协议版本和其他内容用换行(\x0A)分隔。
其次对比一下http、https访问连接服务器过程:
http: TCP三次握手——发送请求数据——后台处理——返回结果
https: TCP三次握手——客户端发起https认证请求(第一步由client发送hello报文并带上相关信息)...
在Nginx没有正确开启支持SSL的情况下,access日志会出现$request字段是十六进制字符串,会不会是Nginx把https认证过程中的第一个hello报文当作http报文进行解析了?
抓包验证截图如下,其中报文的原始数据中(最后一个红框)显示的十六进制与access日志中打印的十六进制几乎完全一样!有兴趣的同学可以自行对比验证一下!
结论
在Nginx没有开启SSL支持的情况下,Nginx将https连接建立过程中的客户端hello报文当作http报文处理,暴力的截取了报文中指定位置的十六进制字符串当作了$request的http请求方法、URL和版本号,所以access日志中会出现十六进制字符串。
那为什么access日志中4个请求的$request开头部分是一致的,后面部分又不一致了呢?分析hello报文格式就知道了!