nginx反向代理https内部定向到http报302的问题及解决

 更新时间:2023年12月22日 15:19:59   作者:shihuc  
这篇文章主要介绍了nginx反向代理https内部定向到http报302的问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

环境信息

Linux:Linux i-8emt1zr1 2.6.32-573.el6.x86_64 #1 SMP Wed Jul 1 18:23:37 EDT 2015 x86_64 x86_64 x86_64 GNU/Linux

nginx:nginx version: openresty/1.9.3.2

Tomcat:Server version: Apache Tomcat/7.0.64

1. 问题描述

我们开发的客服系统,因为消息的到来,有的谷歌浏览器(V62)不支持http的消息提醒,要求https,故而,我们的系统,要将系统改造成https模式,另外,我们的系统,也有必要转化为https,为后续推广做准备。

2. 系统架构

LB+nginx+tomcat集群

3. 当前配置情况

SSL证书配置在LB上,nginx和tomcat服务器上,任然采用http协议通讯。

即LB在接收到客户浏览器https请求消息后,将转发给LB下挂载的nginx上,都是以http的方式转发,nginx对这些请求进行反向代理,代理到后面的tomcat服务器上。

4. 遇到的问题

客服系统,有权限控制,基于tomcat的web应用,用户登录后,执行redirect跳转到指定的服务页面。

就是这个跳转,遇到了问题,redirect在这里都被当做http跳转了。

登录前的样子:

登录后的样子:

问题主要发生在Tomcat7上,验证过tomcat8,是不存在问题的。

5. 如何解决

针对Tomcat7的这个问题,思路很简单,重点是解决redirect的时候,通知客户端浏览器以正确的scheme(https还是http)进行再次发起请求。

问题是, nginx这个时候收到的请求是来自LB的http请求了,怎么弄?其实是有办法的,可以利用HttpRequest中的referer字段,这个字段的含义,自行科普吧。

将referer的请求scheme信息,用来作为当前请求的scheme,如此可以保证所有的请求都是同一个scheme,不会因为redirect而遗漏信息。

nginx里面相应的配置如下:

location /CSS/websocket {
            proxy_pass http://css_ws_svr;
            proxy_set_header Host $host;
            proxy_set_header Remote_Addr $remote_addr;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }

        location /CSS {
            proxy_pass http://css_svr;
            proxy_set_header Host $host;
            proxy_set_header Remote_Addr $remote_addr;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            set $mscheme $scheme;
            if ($http_referer ~* ^https.*) {
                set $mscheme "https";
            }
            proxy_set_header X-Forwarded-Proto $mscheme;
        }

如上配置,经过nginx反向代理后的HttpServletRequest中header部分就带上了字段X-Forwarded-Proto。

另外一方面,就是tomcat里面,要做一个配置,让tomcat在解析请求和做重定向的时候,知道用什么协议。

主要的配置在server.xml里面的Engine下,定义一个Value元素。

具体配置如下:

<Engine name="Catalina" defaultHost="localhost">

      
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <!-- This Realm uses the UserDatabase configured in the global JNDI
             resources under the key "UserDatabase".  Any edits
             that are performed against this UserDatabase are immediately
             available for use by the Realm.  -->
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>

      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
        
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log." suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />

        <Valve className="org.apache.catalina.valves.RemoteIpValve" remoteIpHeader="X-Forwarded-For" protocolHeader="X-Forwarded-Proto" protocolHeaderHttpsValue="https"/>

        <Context path="/CSS" docBase="/home/tomcat/app/cssv2"/>
      </Host>
    </Engine>

这个配置里面,重点是protocolHeader字段,意思就是说,当protocolHeader字段的值为protocolHeaderHttpsValue的https的时候,认为是安全连接,否则就是http的非安全连接。

对应的代码逻辑,可以看org.apache.catalina.valves.RemoteIpValve这个类的源码

public void invoke(org.apache.catalina.connector.Request request, Response response)
 throws IOException, ServletException
{
......
if (protocolHeader != null) {
    String protocolHeaderValue = request.getHeader(protocolHeader);
    if (protocolHeaderValue != null)
    {

        if (protocolHeaderHttpsValue.equalsIgnoreCase(protocolHeaderValue)) {
            request.setSecure(true);
            
            request.getCoyoteRequest().scheme().setString("https");
        
            setPorts(request, httpsServerPort);
        } else {
            request.setSecure(false);
                
            request.getCoyoteRequest().scheme().setString("http");
                
            setPorts(request, httpServerPort);
        }
    }
}
......
}

经过上面的分析和配置修改,最终很灵活的实现https和http同时工作。

搞定这个问题,重点还是要对Http协议工作的流程有所了解,才能很容易的找到解决问题的思路。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Nginx(PHP/fastcgi)的PATH_INFO问题

    Nginx(PHP/fastcgi)的PATH_INFO问题

    PATH_INFO是一个CGI 1.1的标准,经常用来做为传参载体. 比如, 我们可以使用PATH_INFO来代替Rewrite来实现伪静态页面, 另外不少PHP框架也使用PATH_INFO来作为路由载体.
    2011-08-08
  • nginx如何配置参数以及变量

    nginx如何配置参数以及变量

    这篇文章主要介绍了nginx如何配置参数以及变量问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • Nginx服务器下使用rewrite重写url以实现伪静态的示例

    Nginx服务器下使用rewrite重写url以实现伪静态的示例

    这篇文章主要介绍了Nginx服务器下使用rewrite重写url以实现伪静态的示例,这里举了Discuz!和WordPress这两个常用的PHP程序,需要的朋友可以参考下
    2015-12-12
  • nginx proxy_cache批量清除缓存的脚本介绍

    nginx proxy_cache批量清除缓存的脚本介绍

    今天小编就为大家分享一篇关于nginx proxy_cache批量清除缓存的脚本介绍,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-02-02
  • Nginx漏洞复现的问题案例解析

    Nginx漏洞复现的问题案例解析

    这篇文章主要介绍了Nginx解析漏洞复现,这个漏洞其实是由php.ini中cgi.fix pathinfo选项与php-fpm的配置一起导致的,防范的话,只需在php-fpm配置文件中设置security.limit_extensions=.php,重启一下服务即可,本文讲解的非常详细,需要的朋友可以参考下
    2024-01-01
  • Nginx本地目录映射实现代码实例

    Nginx本地目录映射实现代码实例

    这篇文章主要介绍了Nginx本地目录映射实现代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10
  • ELK监控nginx日志的整体流程

    ELK监控nginx日志的整体流程

    这篇文章主要介绍了ELK监控nginx日志总结,整体流程是先把logstash启动,读取nginx日志数据存储到ES中,再用kibana进行统计以及可视化,本文给大家介绍的非常详细,需要的朋友参考下吧
    2022-03-03
  • Nginx开启一个参数就能让你的WEB性能提升3倍的方法

    Nginx开启一个参数就能让你的WEB性能提升3倍的方法

    这篇文章主要介绍了Nginx开启一个参数就能让你的WEB性能提升3倍的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-03-03
  • Nginx日志按日期切割详解(按天切割)

    Nginx日志按日期切割详解(按天切割)

    由于nginx的日志本身只是支持按照server_name或者大小进行划分,对于习惯了apache的按照一个网站每天一个日志的我来说是不可以接受的,所以就实现了按天切割的功能,这篇文章主要介绍了关于Nginx日志按日期切割的相关资料,需要的朋友可以参考下。
    2017-03-03
  • ubuntu16.04下彻底卸载nginx的相关命令

    ubuntu16.04下彻底卸载nginx的相关命令

    nginx是一款自由的、开源的、高性能的HTTP服务器和反向代理服务器;这篇文章主要介绍了ubuntu16.04下彻底卸载nginx的相关命令,需要的朋友可以参考下
    2018-12-12

最新评论