您的位置:首页 > 运维架构 > Nginx

NGINX杂谈——proxy和rewrite的区别

2021-09-15 23:03 2066 查看

概述

使用NGINX服务器进行HTTP报文的处理和转发时,有一些容易混淆的概念。比如像正向代理和反向代理、root和alias、proxy和rewrite。

这篇博客主要想记录一下自己对proxy和rewrite的一些理解。(这里不去考虑rewrite的最后一项参数和区分301、302,建议想看完整细节的看这篇博客,更多关于NGINX的知识可以看官方文档)。

Little NGINX

我用C实现一个阉割版的NGINX,以此来感受proxy和rewrite两个过程中,HTTP报文变化的区别。具体的项目可以看这个代码仓库,我不是依据NGINX的源码进行的代码编写,所以具体的实现上会有所差异,实际使用的功能也没有NGINX那么丰富。还有就是代码虽然套了C的壳,但核心部分是基于Linux C编写的,所以只支持在Linux系统或MacOS等类Unix系统上使用。

  1. 克隆项目

    git clone https://gitee.com/xuanyusan/little_nginx.git
    cd little_nginx
  2. 编译项目

    g++ little_nginx.cpp -o little_nginx
  3. 开启little_nginx

    ./little_nginx

    打印出已配置的服务则运行成功,此时可以在浏览器输入

    localhost:8880
    访问服务。

  4. 开启nodejs koa服务

    cd servers/node_koa/
    npm install
    node index.js

    开启nodejs服务后,在浏览器输入

    localhost:8880/koa/
    可以访问到nodejs的服务。

    在浏览器输入

    localhost:8880/koa/path1
    则可以体验proxy处理重定向的整个流程,little_nginx的stdout也会有对应输出,帮助理解。

proxy和rewrite的区别

proxy和rewrite的区别可以通过下面的两个图进行简单理解。对图中WEB服务的理解应该包括NGINX提供的WEB服务。

Created with Raphaël 2.2.0rewrite客户端客户端NGINXNGINXWEB服务WEB服务request根据rewrite参数构建状态为302的响应报文根据响应报文的Location参数进行重定向生成新的requestresponse Created with Raphaël 2.2.0proxy客户端客户端NGINXNGINXWEB服务WEB服务request根据proxy_pass的规则 修改客户端的requestresponse假设发生了重定向 则根据proxy_redirect的规则 修改response的Location参数

rewrite

从图中可以看出,rewrite必定会发生重定向,因为它会给客户端返回一个302的响应报文,而其中的Location参数就是由一开始请求的path根据rewrite参数处理得到的。

假设nginx配置为:

rewrite ^/(.*) https://www.baidu.com/$1 redirect;

我们通过curl去看HTTP报文的信息则是这样的:

$ curl -v http://localhost/test/
*   Trying ::1:80...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 80 failed: Connection refused
*   Trying 127.0.0.1:80...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET /test/ HTTP/1.1
> Host: localhost
> User-Agent: curl/7.65.3
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 302 Moved Temporarily
< Server: nginx/1.17.3
< Date: Wed, 15 Sep 2021 10:02:04 GMT
< Content-Type: text/html
< Content-Length: 145
< Connection: keep-alive
< Location: https://www.baidu.com/test/
<
<html>
<head><title>302 Found</title></head>
<body>
<center><h1>302 Found</h1></center>
<hr><center>nginx/1.17.3</center>
</body>
</html>
* Connection #0 to host localhost left intact

可以看到相应头的Location参数就是由请求报文的

/test/
保留了
test/
部分并在前面接上
https://www.baidu.com/
得到的。

而这就必须保证rewrite第二个参数的域名或者ip以及端口必须是客户端可以访问的,不行的话,就不能用rewrite。所以rewrite更常用于域名变更和网站搬迁。

proxy

而proxy是不一定会发生重定向的,即使发生重定向,也是由具体的服务决定的,而不是NGINX这个中间代理可以越俎代庖的。NGINX的proxy有两个关键配置,一个是proxy_pass,一个是proxy_redirect,分别对应请求报文和响应报文的处理。

proxy_pass就是修改请求的协议、路径和host,然后发给具体的服务端。proxy_redirect就是在具体服务端发回来一个302响应报文的时候,根据proxy_redirect参数的规则去修改Location。

假设我们现在的机器上,在本地回环的8881端口跑着一个nodejs的服务,然后我们通过机器某个局域网的ip地址去访问80端口的

/test

nodejs服务入口文件如下:

const Koa = require('koa');
const Router = require('koa-router');
const Koa_Logger = require("koa-logger");
const koaBody = require('koa-body')

const app = new Koa();
const router = new Router();
const logger = new Koa_Logger();

router.get("/baseurl", async (ctx)=>{
ctx.body = "<h1>Node Koa</h1>"
});

router.get("/baseurl/path1", async (ctx)=>{
ctx.redirect("/baseurl/path3");
});

router.get("/baseurl/path3", async (ctx)=>{
ctx.body = "<h1>Node Koa Path3</h1>"
});

app.use(logger);
app.use(koaBody());
app.use(router.routes());

app.listen(8881, () => {
console.log('应用已经启动,http://localhost:8881');
});

如果我们只配置了proxy_pass:

proxy_pass http://localhost:8881/baseurl;

访问非重定向的页面是正常的:

$ curl -v http://localhost/test/
*   Trying ::1:80...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 80 failed: Connection refused
*   Trying 127.0.0.1:80...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET /test/ HTTP/1.1
> Host: localhost
> User-Agent: curl/7.65.3
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: nginx/1.17.3
< Date: Wed, 15 Sep 2021 10:28:57 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 17
< Connection: keep-alive
<
* Connection #0 to host localhost left intact
<h1>Node Koa</h1>

因为这个时候我们的HTTP请求的协议、ip和端口被修改为了proxy_pass参数里的

http
localhost
8881
,并且发送给了localhost的8881端口。这时,监听8881端口的服务就可以接收报文并返回结果。

所以proxy_pass的参数就不要求客户端可以访问,只要代理服务器可以访问就可以了。客户甚至不知道他们访问的服务到底是谁提供的,这也是反向代理的一大特点。

但是,如果服务端发生了重定向,结果则如下:

$ curl -v http://localhost/test/path1
*   Trying ::1:80...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 80 failed: Connection refused
*   Trying 127.0.0.1:80...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET /test/path1 HTTP/1.1
> Host: localhost
> User-Agent: curl/7.65.3
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 302 Found
< Server: nginx/1.17.3
< Date: Wed, 15 Sep 2021 10:29:54 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 59
< Connection: keep-alive
< Location: /baseurl/path3
<
* Connection #0 to host localhost left intact
Redirecting to <a href="/baseurl/path3">/baseurl/path3</a>.

可以看到它重定向的结果不是我们预期中的http://localhost/test/path3,而是http://localhost/baseurl/path3。而想要让它重定向到http://localhost/test/path3,我们就不能直接把nodejs扔给我们的响应头原封不动地扔给客户端,而是要用proxy_redirect来指定相应头Location参数的修改规则。比如:

proxy_redirect /baseurl /test;
$ curl -v http://localhost/test/path1
*   Trying ::1:80...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 80 failed: Connection refused
*   Trying 127.0.0.1:80...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET /test/path1 HTTP/1.1
> Host: localhost
> User-Agent: curl/7.65.3
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 302 Found
< Server: nginx/1.17.3
< Date: Wed, 15 Sep 2021 10:37:30 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 59
< Location: http://localhost/test/path3
< Connection: keep-alive
<
* Connection #0 to host localhost left intact
Redirecting to <a href="/baseurl/path3">/baseurl/path3</a>.

这里可以明显看到Location的改变,当然proxy不会去修改报文的body部分,所以body部分还是

/baseurl/path3
。这里万一因为网络原因,重定向没有完成,显示了这个页面,用户又去点击,就会出现错误。NGINX也不是万能的,还是需要我们自己去理解代理背后的逻辑,才能有效应对各种问题。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: