最近通過Nginx反向代理一個(gè)網(wǎng)站,環(huán)境為Flask+uwsgi+Nginx反向代理,當(dāng)部署證書并設(shè)置強(qiáng)制跳轉(zhuǎn)https以后,在瀏覽器輸入:blog.amd5.cn訪問出現(xiàn)了“400 Bad Request The plain HTTP request was sent to HTTPS port”錯(cuò)誤,如下圖:
一開始懷疑自己的配置有問題,反復(fù)確認(rèn)和對比以后,排除配置問題。
于是網(wǎng)上搜索了相關(guān)報(bào)錯(cuò),找到了解決方案,出現(xiàn)這種報(bào)錯(cuò)的主要原因是:
因?yàn)镠TTP請求被發(fā)送到HTTPS端口,這種報(bào)錯(cuò)多出現(xiàn)在Nginx既處理HTTP請求又處理HTTPS請求的情況。
正常80端口訪問應(yīng)該是:http://blog.amd5.cn:80/login
正常開啟HTTPS以后443端口訪問應(yīng)該是:https://blog.amd5.cn:443/login
但是此時(shí)卻變成了: http://blog.amd5.cn:443/login,即HTTP請求被發(fā)送到HTTPS端口。
如果我直接在瀏覽器輸入:https://blog.amd5.cn,卻能正常跳轉(zhuǎn)到https://blog.amd5.cn/login
直接輸入http://blog.amd5.cn/login,也能正常跳轉(zhuǎn)到https://blog.amd5.cn/login
為什么會(huì)有這樣的問題呢,經(jīng)過排查原來是多次重定向?qū)е碌?,主要是因?yàn)樵谑褂肍lask-Login驗(yàn)證登錄的時(shí)候,如果未登錄,會(huì)重定向到登錄頁面。
通過瀏覽器開發(fā)者工具,可以看到,當(dāng)我瀏覽器輸入blog.amd5.cn,第一次重定向?yàn)閔ttp->https:
Request URL: https://blog.amd5.cn/ Request Method: GET Status Code: 302 FOUND Remote Address: 127.0.0.1:443 Referrer Policy: strict-origin-when-cross-origin
然后第二次重定向?yàn)椋?->/login:
Request URL: http://blog.amd5.cn:443/login Request Method: GET Status Code: 400 Bad Request Remote Address: 127.0.0.1:443 Referrer Policy: strict-origin-when-cross-origin
簡單來說就是:當(dāng)?shù)谝淮握埱笤噲D通過HTTP訪問網(wǎng)站blog.amd5.cn,這個(gè)請求被重定向到HTTPS。于是Nginx預(yù)計(jì)使用SSL(443端口)交互,但原來的請求(通過端口80接收,即檢查到未登錄,需要從/跳轉(zhuǎn)到/login)是普通的HTTP請求,于是會(huì)產(chǎn)生錯(cuò)誤。
解決方法是在原來的配置上面加兩個(gè)參數(shù):
proxy_set_header X-Forwarded-Proto https; # X-Forwarded-Proto(XFP)報(bào)頭是用于識(shí)別協(xié)議HTTP或HTTPS的,即用戶客戶端實(shí)際連接到代理或負(fù)載均衡的標(biāo)準(zhǔn)報(bào)頭。 proxy_redirect http:// https:// # proxy_redirect 該指令用來修改被代理服務(wù)器返回的響應(yīng)頭中的Location頭域和“refresh”頭域,也就是把http協(xié)議改成https協(xié)議。
添加以后完整的配置如下:
server { listen 443 ssl; ssl_certificate /conf/ssl/blog.amd5.cn.pem; ssl_certificate_key /conf/ssl/blog.amd5.cn.key; ssl_session_timeout 5m; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; server_name blog.amd5.cn; #代理配置 location /{ proxy_set_header X-Original-Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host blog.amd5.cn:$server_port; proxy_set_header X-Forwarded-Proto https; proxy_pass http://127.0.0.1:5000/; proxy_redirect http:// https://; } } server{ listen 80 ; server_name blog.amd5.cn; #代理配置 location /{ proxy_set_header X-Original-Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host blog.amd5.cn:$server_port; proxy_pass http://127.0.0.1:5000/; } add_header X-Cache $upstream_cache_status; proxy_cache_key $host$uri$is_args$args; if ($scheme = http) { return 301 https://$host$uri?$args; } }
再次測試訪問正常:
兩次請求:
Request URL: https://blog.amd5.cn/ Request Method: GET Status Code: 302 FOUND Remote Address: 127.0.0.1:443 Referrer Policy: strict-origin-when-cross-origin
Request URL: https://blog.amd5.cn/login Request Method: GET Status Code: 200 OK Remote Address: 127.0.0.1:443 Referrer Policy: strict-origin-when-cross-origin