魔法施展中...

dokku下nginx两级转发https请求时的坑

5 分钟
...

最近遇到一个让我排查了好一阵子的问题。

问题描述:

我是用dokku来部署的,因此nginx 生成了应用的nginx.conf,这个应用工作于7000端口。 我自己手工写了一个配置文件,监听443端口来服务https请求,这个服务就是定制了一些转发,期中"/"路径是转发给7000端口的。 后端应用是rails写的,有大量的redirect请求的场景。 完整的路径是:

用户 => nginx 443 => nginx(7000)=> rails

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配置的这一行:

proxy_set_header X-Forwarded-Proto $scheme;

nginx 443的host收到用户的https请求,将协议改成http转发给7000,同时加上了X-Forwarded-Proto: https 这个头。 nginx 7000端口的host收到的是一个http请求,带有一个X-Forwarded-Proto: https的头,但是整个请求是http的,不是https的。这个时$schemehttp,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 这个文件。 打开这个文件,找到

{{ else if eq $scheme "https"}}

这后面,将这一行

proxy_set_header X-Forwarded-Proto {{ $.PROXY_X_FORWARDED_PROTO }};

强制改一下,写死https。

 proxy_set_header X-Forwarded-Proto 'https';
📮 订阅更新
每周收到最新文章推送,不错过精彩内容

💡 我们尊重您的隐私,不会将邮箱用于其他用途

加载中...