1. 博客/

Nginx的proxy_redirect配置

·356 字·2 分钟
Nginx

前言
#

当nginx代理的后端服务器有301、302跳转时,需要注意指定proxy_set_header Host请求头部,会直接影响nginx反馈给客户端的URL请求,要结合实际需求调整proxy_set_header Host和proxy_redirect将被代理服务器设置在response header中的Location做转换。

搭建测试环境
#

1.测试架构规划

  • 主机192.168.196.134
    • nginx虚拟主机www.test.com;监听端口31001;转发文根 /th;后端server 192.168.196.135:31009
    • nginx虚拟主机192.168.196.134;监听端口31000;转发文根 *.jsp;后端server 192.168.196.136:31005
  • 主机192.168.196.135
    • nginx虚拟主机 192.168.196.135;监听端口31009;转发文根 /th/ft;后端server 192.168.196.134:31000
  • 主机192.168.196.136
    • tomcat server; 监听端口31005

192.168.196.134:31001/th —> 192.168.196.135:31009/th/ft –> 192.168.196.134:31000 *.jsp –> 192.168.196.136:31005

2.192.168.196.136上部署war包

sh-4.2$ cat test.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%
out.println("Protocol: " + request.getProtocol());  
out.println("Server Name: " + request.getServerName());  
out.println("Server Port: " + request.getServerPort());  
out.println("Remote Addr: " + request.getRemoteAddr());  
out.println("Remote Host: " + request.getRemoteHost());  
out.println("Request URL: " + request.getRequestURL());  
out.println("Host: " + request.getHeader("Host"));  
out.println("Connection : " + request.getHeader("Connection"));  
%>

$jar cvf th.war th/ft/test.jsp

3.192.168.196.134上配置 nginx

upstream backendsrv {
	server 192.168.196.136:31005;
}
server {
    listen       31001;
    server_name  www.test.com;
    location /th {
         root   html;
         index  index.html index.htm;
         proxy_pass http://192.168.196.135:31009;
         #proxy_redirect ~^http://www.test.com:31009(.*) http://$host:$server_port$1;
         proxy_set_header Host $http_host;
      }
}

server {
   listen 31000;
   server_name www.firsthost.com 192.168.196.134;
   location /th/ft {
        root   html;
        index  index.html index.htm; 
   }
   location ~* .jsp$ {
     proxy_pass http://backendsrv; 
     proxy_set_header Host $http_host;
   }
}

4.192.168.196.135上配置nginx

upstream backendnginx {
    server 192.168.196.134:31000;
}
server {
    listen       31009;
    server_name  192.168.196.135;
    location /th/ft {
        root   html;
        index  index.html index.htm;
        proxy_pass http://backendnginx;
        #proxy_redirect ~^http://www.test.com:31000(.*) http://$host:$server_port$1;
        proxy_set_header Host $http_host;
    } 
    
    location /th {
        root   html;
        index  index.html index.htm;
        
    }
}

5.按照当前配置在客户端访问:

$curl -L www.test.com:31001/th/ft/test.jsp 
Protocol: HTTP/1.0
Server Name: www.test.com
Server Port: 31001
Remote Addr: 192.168.196.134
Remote Host: 192.168.196.134
Request URL: http://www.test.com:31001/th/ft/test.jsp
Host: www.test.com:31001                                 
Connection : close

由于每个转发文根下都设置了Host请求头(proxy_set_header Host $http_host;),最终tomcat应用服务器从请求中获取的Host信息为面向客户端的第一级代理nginx的虚拟主机server_name及监听端口

$curl -I www.test.com:31001/th 
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Thu, 21 Mar 2019 02:50:47 GMT
Content-Type: text/html
Content-Length: 178
Connection: keep-alive
Location: http://www.test.com:31009/th/

$curl -I www.test.com:31001/th/ft
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Thu, 21 Mar 2019 02:50:51 GMT
Content-Type: text/html
Content-Length: 178
Connection: keep-alive
Location: http://www.test.com:31000/th/ft/

301跳转时,返回给客户端重新发送请求的Location中URL有问题。 后端server的响应头中的location字段信息为: 1.host值被设置为第一个转发Location所在虚拟主机的server_name www.test.com

这里要注意的时,如果nginx转发Location中未设置Host请求头,则host值为proxy_pass后指定的地址

2.端口被设置为每个转发Location中proxy_pass/upstream定义的端口

此时要用proxy_redirect将被代理服务器设置在response header中的Location做转换。官方解释:

proxy_redirect 语法:proxy_redirect [ default|off|redirect replacement ]  默认值:proxy_redirect default  使用字段:http, server, location  如果需要修改从被代理服务器传来的应答头中的"Location"和"Refresh"字段,可以用这个指令设置。

6.根据上面的访问结果,对应跳转地址:/th –> /th/和/th/ft –> /th/ft/

在相应的转发Location下,分别添加:

  • proxy_redirect ~^http://www.test.com:31009(.*) http://$host:$server_port$1;
  • proxy_redirect ~^http://www.test.com:31000(.*) http://$host:$server_port$1;

$host$server_port 分别为转发文根Location所在虚拟主机的server_name和listen port

7.reload Nginx后,从客户端访问

$curl -I www.test.com:31001/th 
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Thu, 21 Mar 2019 03:15:10 GMT
Content-Type: text/html
Content-Length: 178
Connection: keep-alive
Location: http://www.test.com:31001/th/

$curl -I www.test.com:31001/th/ft
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Thu, 21 Mar 2019 03:18:22 GMT
Content-Type: text/html
Content-Length: 178
Connection: keep-alive
Location: http://www.test.com:31001/th/ft

301跳转时,返回给客户端重新发送请求的Location中URL已正确显示

最佳实践
#

依据转发Location中Host请求头是否被指定,来修改proxy_redirect的值:

  • 如果Host请求头未指定过,则后端server返回的Location为nginx中proxy_pass后的servername/IPproxy_pass后的端口或upstream中转发端口的拼接

  • 如果Host请求头在当前转发Location指定

    • Host在nginx收到的请求中未指定过,则后端server返回的Location为nginx当前转发Location的server_nameproxy_pass后的端口或upstream中转发端口的拼接
    • Host在nginx收到的请求中已指定,则后端server返回的Location为nginx接收的请求中Hostproxy_pass后的端口或upstream中转发端口的拼接

Related

nginx请求头X-Real-IP与X-Forwarded-For
·455 字·3 分钟
Nginx
Nginx请求头proxy_set_header
·229 字·2 分钟
Nginx
P2P文件与镜像分发开源解决方案Dragonfly安装部署
·859 字·5 分钟
Docker