dokku下nginx两级转发https请求时的坑
最近遇到一个让我排查了好一阵子的问题。
问题描述:
我是用dokku来部署的,因此nginx 生成了应用的nginx.conf,这个应用工作于7000端口。
我自己手工写了一个配置文件,监听443端口来服务https请求,这个服务就是定制了一些转发,期中”/“路径是转发给7000端口的。
后端应用是rails写的,有大量的redirect请求的场景。
完整的路径是:
1 |
|
nginx 443转发到7000端口的时候,是用的proxy_pass http://localhost:7000/
这个设定,从https转到了http转发。
现在问题来了,当有转发的时候,浏览器页面是https://a.example.com/
,收到的是location: http://a.example.com/somepage
这个。这时候浏览器拒绝响应了,不执行这个来自服务端的重定向。可能是一个安全策略,https页面不允许重定向到http页面。
于是我把rails的 force_ssl 设置为true了。结果问题更严重了,所有请求都报错,说重定向次数太多。
排查最终发现问题出在Nginx配置的这一行:
1 |
|
nginx 443的host收到用户的https请求,将协议改成http转发给7000,同时加上了X-Forwarded-Proto: https
这个头。
nginx 7000端口的host收到的是一个http请求,带有一个X-Forwarded-Proto: https
的头,但是整个请求是http的,不是https的。这个时$scheme
是http
,nginx 这时发给rails 的是 X-Forwarded-Proto: http
这个头。
于是rails的强制转https的部分工作了,给客户端下发了一个Location: https://${host}/${path}
的响应。
但是请求本身已经是https了啊,于是就不断地循环这个过程了。
解决方案:
将第二层也就是nginx监听7000端口的配置,从proxy_set_header X-Forwarded-Proto $scheme;
直接改成proxy_set_header X-Forwarded-Proto "https";
。方案粗暴了一些, 但是好像也没有好的办法。
但是,dokku应用的nginx配置文件,在每次deploy之后都会重新生成,因此我们要找个稳妥的方案,保证生成的nginx配置就写的是https而不是$scheme 这个变量。
dokku的nginx模板,其实都是来自于/var/lib/dokku/plugins/enabled/nginx-vhosts/templates/nginx.conf.sigil 这个文件。
打开这个文件,找到
这后面,将这一行
强制改一下,写死https。
1 |
|