背景
心血來潮想給自己的小網站加上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報文格式就知道了!