一、nginx rewrite 与 proxy_pass区别
rewrite
和 proxy_pass
都是 Nginx 的常用指令,但它们的功能和使用场景有所不同。
1、rewrite
:是一个用来修改请求 URI 的指令。在 Nginx 收到一个请求后,rewrite
指令可以基于特定的条件改变这个请求的 URI,可能是改变文件路径,可能是重定向到一个新的地址。基本的语法为 rewrite regex replacement [flag]
其中 regex
是要匹配的正则表达式,replacement
是匹配的结果要被替换的内容。例如,rewrite ^(/download/.*)/media/(.*)$ $1/mp3/$2
,这个会把 "/download/media/abc.mp3"这样的 URL 替换为 "/download/mp3/abc.mp3"。负载越大,对硬件的要求就越高。
2、proxy_pass
:这个指令告诉 Nginx 将收到的请求转发到其他的服务器。在做反向代理时经常会用到此命令。例如,proxy_pass http://localhost:8000;
这个配置就会把所有请求转发到运行在 localhost:8000 上的服务器。负载越高,对网络的需求就越大。
而它们之间的主要区别就是,rewrite
是对请求 URL 的修改,而不改变请求的目的地,而 proxy_pass
是不改变 URL,但是改变了请求的后端服务器,这两个指令通常结合起来使用,用以实现一些特定的需求。在实际运用中我们经常看到这两者一起使用,以满足定制化路由的逻辑。
例如:
location / {
rewrite ^/abc/(.*)$ /xyz/$1 break;
proxy_pass http://localhost:8000;
}
这个配置中,客户端请求的 URL "/abc/test" 会被重写为 "/xyz/test" ,然后再被转发到 localhost:8000 这个服务器上。
问题一:问题来了,那么,使用rewrite后,假设我的配置是location / { rewrite ^/abc/(.*)$ /xyz/$1 break; proxy_pass http://localhost:8000; },那么我在浏览器输入http://10.10.1.2/abc/mm,访问后,浏览器地址是否会变成http://10.10.1.2/xyz/mm?
不会,Nginx的rewrite指令在这种情况下只会更改传递给后端服务器的URI,并不会影响浏览器的地址栏。在浏览器地址栏输入http://10.10.1.2/abc/mm,服务器会接收到这个请求并通过rewrite规则将其更改为/xyz/mm,然后将其转发到proxy_pass设置的地址http://localhost:8000/xyz/mm。但这并不会更改浏览器的地址栏。
如果你想要在rewrite后更改浏览器的地址栏,你需要发送一次HTTP重定向。例如,以下配置可以在rewrite时发送HTTP 301永久重定向:
rewrite ^/abc/(.*)$ /xyz/$1 permanent;
在此情况下,当浏览器尝试访问http://10.10.1.2/abc/mm,服务器会返回HTTP 301响应和新地址http://10.10.1.2/xyz/mm。然后浏览器会在地址栏更新新地址并请求新地址。请注意,这将使浏览器显示新URL,但也将导致两次请求——一次是原始请求,另一次是重定向到新URL的请求。
问题二:那么,使用rewrite后,假设我的配置是location / { rewrite ^/abc/(.*)$ /xyz/$1 break; },那么我在浏览器输入http://10.10.1.2/abc/mm,访问后,浏览器地址是否会变成http://10.10.1.2/xyz/mm?
不会,同样的,即使你没有使用 proxy_pass
,Nginx 的 rewrite
指令也只是在服务器内部重写 URL,而不会影响浏览器地址栏显示的 URL。
在你的例子中,当你在浏览器输入 http://10.10.1.2/abc/mm,Nginx 会接收到这个请求,并将请求 URL 通过 rewrite 规则改写为 "/xyz/mm"。然后,Nginx 会在本地查找这个新的 URL 对应的资源。这个过程对客户端浏览器是不可见的,因此浏览器地址栏还是会显示原来输入的 URL。
如果你要想改变浏览器的地址栏显示的 URL,你需要显式地发出一个重定向响应,比如使用 HTTP 的 301 或 302 状态码。这可以通过在 rewrite
指令后面添加 redirect
或 permanent
标志来实现。例如,
rewrite ^/abc/(.*)$ /xyz/$1 permanent;
这样,当一个请求匹配这个 rewrite
规则时,Nginix 会向客户端浏览器发出一个 301(永久重定向)响应,浏览器接收到这个响应之后,会自动将地址变为新的 URL 并重新发送请求。
问题三:rewrite ^/abc/(.*)$ /xyz/$1 permanent;和rewrite ^/abc/(.*)$ /xyz/$1 redirect;是什么区别
rewrite ^/abc/(.*)$ /xyz/$1 permanent;
和 rewrite ^/abc/(.*)$ /xyz/$1 redirect;
这两个Nginx配置的主要区别在于重定向的HTTP状态码。
-
permanent: 生成一个HTTP 301 Moved Permanently响应。这个响应是永久性的,告诉客户端(比如浏览器)这个资源已经被永久的移动到了新的位置,以后应该直接去新的位置查找这个资源。
-
redirect:生成一个HTTP 302 Moved Temporarily响应。这个响应告诉客户端这个资源被暂时移动到了新的位置,但是可能以后还会回到原来的位置。
因此,在实际使用中,你应该根据实际情况选择恰当的响应。如果你知道资源永久的移动到了新的位置,那么应该使用permanent;如果你知道资源只是暂时移动到新的位置,那么应该使用redirect。
二、proxy_pass会改变路径地址吗?
不会。
如下配置,^~ /main 表示当请求的 URI 以 /main
开头时,将应用这个 location
块中的配置。
proxy_pass
指令用于将请求转发到指定的上游服务器(在这个例子中是 http://hubble-portal
)。当请求被转发到上游服务器时,原始请求的路径(/main
及其子路径)会被保留,并不会发生变化。这意味着,如果客户端请求的是 /main/some/path
,那么这个请求会被转发到 hubble-portal
服务器,但路径仍然是 /main/some/path
。
location ^~ /main {
proxy_pass http://hubble-portal;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
三、配置分析
location ^~ /api/cm/idc {
proxy_pass http://hubble-api-host;
}
location ^~ /api/alarm/alarmquery {
proxy_pass http://hubble-alarm-query/api/biz/bq/alarm/alarmquery;
}
分析下这两个配置:
第二个配置是将用户访问的原url(/api/alarm/alarmquery)转到访问另一个url(/api/biz/bq/alarm/alarmquery)。
第一个是转到访问hubble-api-host服务的/api/cm/idc,url没有变,即转发后的路径是:http://hubble-api-host/api/cm/idc
为啥第一个不配置成如下方式:
location ^~ /api/cm/idc {
proxy_pass http://hubble-api-host/api/cm/idc;
}
原因如下:
第一个配置:
location ^~ /api/cm/idc {
proxy_pass http://hubble-api-host;
}
这个配置使用了^~
修饰符,这意味着对于所有以/api/cm/idc
开头的请求,这个location块将会优先于其他正则匹配的location块生效。请求被转发到http://hubble-api-host
,转发时不会改变原请求的URI。
第二个配置:
location ^~ /api/alarm/alarmquery {
proxy_pass http://hubble-alarm-query/api/biz/bq/alarm/alarmquery;
}
这个配置同样使用了^~
修饰符,它将以/api/alarm/alarmquery
开头的请求转发到新的路径http://hubble-alarm-query/api/biz/bq/alarm/alarmquery
。在转发时,原始请求的URI会被替换为proxy_pass中指定的新URI。
你问到为什么第一个配置不写成:
location ^~ /api/cm/idc {
proxy_pass http://hubble-api-host/api/cm/idc;
}
原因是这样做的话,请求/api/cm/idc
会被转发为http://hubble-api-host/api/cm/idc/api/cm/idc
,导致URI重复而出现错误。当proxy_pass
使用一个不带路径的URL,如http://hubble-api-host
时,Nginx会将原始请求的URI不变地传递给上游服务器。这样若上游服务器具体API路径与代理配置路径一致,那么不需要在proxy_pass
中再次指定。
为了避免错误和混淆,如果需要在转发时保持原始URI不变,则应该使用域名而不是完整的URL路径作为proxy_pass
的参数。如果需要修改转发的URI,再在proxy_pass
参数后面加上新的路径。
结合第二个配置,如果你想重写目标的URI路径,并且希望保留请求的其他部分(例如查询参数),你可以使用下面的配置方法:
location ^~ /api/alarm/alarmquery {
rewrite ^/api/alarm/alarmquery(/.*)$ $1 break;
proxy_pass http://hubble-alarm-query/api/biz/bq/alarm/alarmquery;
}
这样就能够将用户的请求从/api/alarm/alarmquery
重写到/api/biz/bq/alarm/alarmquery
,同时保留如/api/alarm/alarmquery?param=value
这种请求中的查询参数。不过具体是否需要这样做,还需要依据你的实际应用场景及上游服务器的期望请求形式来决定。
总结:
如果proxy_pass配置中不带具体的uri路径,则将原url路径加上。
如果proxy_pass配置中带了具体的uri路径,则直接使用替换后的url路径。
四、分析下此配置
location ^~ /m/ {
proxy_pass http://hubble_gateway_hubble-static/m/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
如上配置后,假设有一个客户端请求为:
http://yourdomain.com/m/page.html
由于配置了上述的 location
块,Nginx 会代理这个请求到上游服务器。完成后的上游请求会变成:
http://hubble_gateway_hubble-static/m/page.html
相对应的,如果是如下配置:
location ^~ /api/alarm/alarmquery {
proxy_pass http://hubble-alarm-query/api/biz/bq/alarm/alarmquery;
}
针对这个配置,当接收到请求:
http://yourdomain.com/api/alarm/alarmquery/details?id=1234
它会被 Nginx 处理并代理转发到如下地址:
http://hubble-alarm-query/api/biz/bq/alarm/alarmquery/details?id=1234