nginx之proxy_pass指令
2019-04-20 16:44:44 阿炯

nginx中有两个模块都有proxy_pass指令,这两个模块分别如下。

1、ngx_http_proxy_module的proxy_pass:

语法: proxy_pass URL;
场景: location, if in location, limit_except
说明: 设置后端代理服务器的协议(protocol)和地址(address),以及location中可以匹配的一个可选的URI。协议可以是"http"或"https"。地址可以是一个域名或ip地址和端口,或者一个 unix-domain socket 路径。  
详见官方文档: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass

2、ngx_stream_proxy_module的proxy_pass:

语法: proxy_pass address;
场景: server
说明: 设置后端代理服务器的地址。这个地址(address)可以是一个域名或ip地址和端口,或者一个 unix-domain socket路径。  
详见官方文档: http://nginx.org/en/docs/stream/ngx_stream_proxy_module.html#proxy_pass

两个proxy_pass的联系和区别

在两个模块中,proxy_pass都是用来做后端代理的指令。

ngx_stream_proxy_module模块的proxy_pass指令只能在server段使用使用,只需要提供域名或ip地址和端口。可以理解为端口转发,可以是tcp端口,也可以是udp端口。ngx_http_proxy_module模块的proxy_pass指令需要在location段,location中的if段,limit_except段中使用,处理需要提供域名或ip地址和端口外,还需要提供协议,如"http"或"https",还有一个可选的uri可以配置。

proxy_pass的具体用法示例

ngx_stream_proxy_module模块的proxy_pass指令

server {
    listen 127.0.0.1:12345;
    proxy_pass 127.0.0.1:8080;
}

server {
    listen 12345;
    proxy_connect_timeout 1s;
    proxy_timeout 1m;
    proxy_pass example.com:12345;
}

server {
    listen 53 udp;
    proxy_responses 1;
    proxy_timeout 20s;
    proxy_pass dns.example.com:53;
}

server {
    listen [::1]:12345;
    proxy_pass unix:/tmp/stream.socket;
}

ngx_http_proxy_module模块的proxy_pass指令
这是本文要着重介绍的地方,请看下文。

Nginx配置proxy_pass转发路径问题

在nginx中配置proxy_pass时,如果是按照^~匹配路径时,要注意proxy_pass后的url最后的'/',当加上了'/',相当于是绝对根路径,则nginx不会把location中匹配的路径部分代理走;如果没有/,则会把匹配的路径部分也给代理走。

location ^~ /static_js/{
proxy_cache js_cache;
proxy_set_header Host js.test.com;
proxy_pass http://js.test.com/;
}

如上面的配置,如果请求的url是http://servername/static_js/test.html
会被代理成http://js.test.com/test.html

而如果这么配置
location ^~ /static_js/{
proxy_cache js_cache;
proxy_set_header Host js.test.com;
proxy_pass http://js.test.com;
}

则会被代理到http://js.test.com/static_js/test.htm

当然,我们可以用如下的rewrite来实现/的功能

location ^~ /static_js/{
proxy_cache js_cache;
proxy_set_header Host js.test.com;
rewrite /static_js/(.+)$ /$1 break;
proxy_pass http://js.test.com;
}


proxy_pass反向代理配置中url后面加不加'/'的示例说明

这里有location与proxy_pass后面加不加'/'的两种情形。

重点:配置proxy_pass时,当在后面的url加上了'/',相当于是绝对根路径,则nginx不会把location中匹配的路径部分代理走;如果没有/,则会把匹配的路径部分也给代理走。

情况说明:源站:192.168.8.1:8090,代理:192.168.8.90:81。

$ curl -v 'http://192.168.8.1:8090'
* Rebuilt URL to: http://192.168.8.1:8090/
*   Trying 192.168.8.1...
* Connected to 192.168.8.1 (192.168.8.1) port 8090 (#0)
> GET / HTTP/1.1
> Host: 192.168.8.1:8090
> User-Agent: curl/7.45.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.12.2
< Date: Sat, 20 Apr 2019 07:54:23 GMT
< Content-Type: text/html; charset=utf8
< Content-Length: 117
< Last-Modified: Sat, 20 Apr 2019 07:22:29 GMT
< Connection: keep-alive
< ETag: "5cbac8b5-75"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome</title>
</head>
<body>
<h1>Welcome to freeoa home.</h1>
</body>
</html>
* Connection #0 to host 192.168.8.1 left intact

在代理nginx上进行配置,不要使用浏览器来看结构,建议使用curl工具,因为这样非常直观。

1)第一种情况:proxy_pass 后加'/'。
location  /proxy/ {
    proxy_pass http://192.168.8.1:8090/;
}

匹配的proxy目录不需要存在根目录里面甚至不需要根目录。注意,终端里如果访问http://192.168.1.23/proxy(即后面不带"/"),则会访问失败!因为proxy_pass配置的url后面加了"/"。

$ curl -v 'http://192.168.8.90:81/proxy/'
*   Trying 192.168.8.90...
* Connected to 192.168.8.90 (192.168.8.90) port 81 (#0)
> GET /proxy/ HTTP/1.1
> Host: 192.168.8.90:81
> User-Agent: curl/7.45.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.9.15
< Date: Sat, 20 Apr 2019 07:58:36 GMT
< Content-Type: text/html; charset=utf8
< Content-Length: 117
< Connection: keep-alive
< Last-Modified: Sat, 20 Apr 2019 07:22:29 GMT
< ETag: "5cbac8b5-75"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome</title>
</head>
<body>
<h1>Welcome to freeoa home.</h1>
</body>
</html>
* Connection #0 to host 192.168.8.90 left intact

$ curl -v 'http://192.168.8.90:81/proxy'
*   Trying 192.168.8.90...
* Connected to 192.168.8.90 (192.168.8.90) port 81 (#0)
> GET /proxy HTTP/1.1
> Host: 192.168.8.90:81
> User-Agent: curl/7.45.0
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< Server: nginx/1.9.15
< Date: Sat, 20 Apr 2019 07:58:51 GMT
< Content-Type: text/html
< Content-Length: 185
< Location: http://192.168.8.90:81/proxy/
< Connection: keep-alive
<
<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.9.15</center>
</body>
</html>
* Connection #0 to host 192.168.8.90 left intact

浏览器里访问http://192.168.8.90:81/proxy的时候,会自动加上"/"(同理是由于proxy_pass配置的url后面加了"/")。

2)第二种情况,proxy_pass 后面不加'/'。

location  /proxy/ {
proxy_pass http://192.168.8.1:8090;
}

$ curl -v 'http://192.168.8.90:81/proxy/'
*   Trying 192.168.8.90...
* Connected to 192.168.8.90 (192.168.8.90) port 81 (#0)
> GET /proxy/ HTTP/1.1
> Host: 192.168.8.90:81
> User-Agent: curl/7.45.0
> Accept: */*
>
< HTTP/1.1 404 Not Found
< Server: nginx/1.9.15
< Date: Sat, 20 Apr 2019 08:02:29 GMT
< Content-Type: text/html; charset=utf8
< Content-Length: 169
< Connection: keep-alive
<
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.12.2</center>
</body>
</html>
* Connection #0 to host 192.168.8.90 left intact

$ curl -v 'http://192.168.8.90:81/proxy'
*   Trying 192.168.8.90...
* Connected to 192.168.8.90 (192.168.8.90) port 81 (#0)
> GET /proxy HTTP/1.1
> Host: 192.168.8.90:81
> User-Agent: curl/7.45.0
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< Server: nginx/1.9.15
< Date: Sat, 20 Apr 2019 08:02:41 GMT
< Content-Type: text/html
< Content-Length: 185
< Location: http://192.168.8.90:81/proxy/
< Connection: keep-alive
<
<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.9.15</center>
</body>
</html>
* Connection #0 to host 192.168.8.90 left intact

这两种请求方式都会失败,但原因各异。这样配置后,访问http://192.168.8.90:81/proxy/就会被反向代理到http://192.168.8.1:8090/proxy/,而在8.1上这个目录是不存在的。

3)第三种情况
在源站8.1上配置好fa目录及其下的index.html
$ curl -v 'http://192.168.8.1:8090/fa/'
*   Trying 192.168.8.1...
* Connected to 192.168.8.1 (192.168.8.1) port 8090 (#0)
> GET /fa/ HTTP/1.1
> Host: 192.168.8.1:8090
>
< HTTP/1.1 200 OK
< Server: nginx/1.12.2
< Date: Sat, 20 Apr 2019 08:09:13 GMT
< Content-Type: text/html; charset=utf8
< Content-Length: 16
< Last-Modified: Sat, 20 Apr 2019 08:08:07 GMT
< Connection: keep-alive
< ETag: "5cbad367-10"
< Accept-Ranges: bytes
<
sec dir for fa.
* Connection #0 to host 192.168.8.1 left intact

location  /proxy/ {
proxy_pass http://192.168.8.1:8090/fa/;
}

$ curl -v 'http://192.168.8.90:81/proxy/'
*   Trying 192.168.8.90...
* Connected to 192.168.8.90 (192.168.8.90) port 81 (#0)
> GET /proxy/ HTTP/1.1
> Host: 192.168.8.90:81
> User-Agent: curl/7.45.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.9.15
< Date: Sat, 20 Apr 2019 08:10:41 GMT
< Content-Type: text/html; charset=utf8
< Content-Length: 16
< Connection: keep-alive
< Last-Modified: Sat, 20 Apr 2019 08:08:07 GMT
< ETag: "5cbad367-10"
< Accept-Ranges: bytes
<
sec dir for fa.
* Connection #0 to host 192.168.8.90 left intact

4)第四种情况:相对于第三种配置的url不加'/'

location  /proxy/ {
proxy_pass http://192.168.8.1:8090/fa;
}

$ curl -v 'http://192.168.8.90:81/proxy/'
*   Trying 192.168.8.90...
* Connected to 192.168.8.90 (192.168.8.90) port 81 (#0)
> GET /proxy/ HTTP/1.1
> Host: 192.168.8.90:81
> User-Agent: curl/7.45.0
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< Server: nginx/1.9.15
< Date: Sat, 20 Apr 2019 08:11:47 GMT
< Content-Type: text/html
< Content-Length: 185
< Location: http://192.168.8.90:81/proxy//
< Connection: keep-alive
<
<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.12.2</center>
</body>
</html>
* Connection #0 to host 192.168.8.90 left intact

上面配置后,访问http://192.168.8.90:81/proxy/index.html就会被代理到http://192.168.8.1:8090/faindex.html。不能直接访问http://192.168.8.90:81/proxy/,后面就算是默认的index.html文件也要跟上,否则访问失败!

上面四种方式都是匹配的path路径后面加"/",下面说下path路径后面不带"/"的情况:

1)第一种情况,proxy_pass后面url带"/":

location  /proxy {
proxy_pass http://192.168.8.1:8090/;
}

$ curl -v 'http://192.168.8.90:81/proxy/'
*   Trying 192.168.8.90...
* Connected to 192.168.8.90 (192.168.8.90) port 81 (#0)
> GET /proxy/ HTTP/1.1
> Host: 192.168.8.90:81
> User-Agent: curl/7.45.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.9.15
< Date: Sat, 20 Apr 2019 08:20:12 GMT
< Content-Type: text/html; charset=utf8
< Content-Length: 117
< Connection: keep-alive
< Last-Modified: Sat, 20 Apr 2019 07:22:29 GMT
< ETag: "5cbac8b5-75"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome</title>
</head>
<body>
<h1>Welcome to freeoa home.</h1>
</body>
</html>
* Connection #0 to host 192.168.8.90 left intact

$ curl -v 'http://192.168.8.90:81/proxy'
*   Trying 192.168.8.90...
* Connected to 192.168.8.90 (192.168.8.90) port 81 (#0)
> GET /proxy HTTP/1.1
> Host: 192.168.8.90:81
> User-Agent: curl/7.45.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.9.15
< Date: Sat, 20 Apr 2019 08:20:15 GMT
< Content-Type: text/html; charset=utf8
< Content-Length: 117
< Connection: keep-alive
< Last-Modified: Sat, 20 Apr 2019 07:22:29 GMT
< ETag: "5cbac8b5-75"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome</title>
</head>
<body>
<h1>Welcome to freeoa home.</h1>
</body>
</html>
* Connection #0 to host 192.168.8.90 left intact

2)第二种情况,proxy_pass后面url不带'/'

location  /proxy {
proxy_pass http://192.168.8.1:8090;
}

$ curl -v 'http://192.168.8.90:81/proxy'
*   Trying 192.168.8.90...
* Connected to 192.168.8.90 (192.168.8.90) port 81 (#0)
> GET /proxy HTTP/1.1
> Host: 192.168.8.90:81
> User-Agent: curl/7.45.0
> Accept: */*
>
< HTTP/1.1 404 Not Found
< Server: nginx/1.9.15
< Date: Sat, 20 Apr 2019 08:21:50 GMT
< Content-Type: text/html; charset=utf8
< Content-Length: 169
< Connection: keep-alive
<
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.12.2</center>
</body>
</html>
* Connection #0 to host 192.168.8.90 left intact

curl -v 'http://192.168.8.90:81/proxy/'与上同为404。

3)第三种情况

location  /proxy {
proxy_pass http://192.168.8.1:8090/fa/;
}

$ curl -v 'http://192.168.8.90:81/proxy/fa'
*   Trying 192.168.8.90...
* Connected to 192.168.8.90 (192.168.8.90) port 81 (#0)
> GET /proxy/fa HTTP/1.1
> Host: 192.168.8.90:81
> User-Agent: curl/7.45.0
> Accept: */*
>
< HTTP/1.1 404 Not Found
< Server: nginx/1.9.15
< Date: Sat, 20 Apr 2019 08:24:22 GMT
< Content-Type: text/html; charset=utf8
< Content-Length: 169
< Connection: keep-alive
<
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.12.2</center>
</body>
</html>
* Connection #0 to host 192.168.8.90 left intact

curl -v 'http://192.168.8.90:81/proxy/fa/'与上同为404。

4)第四种情况:相对于第三种配置的url不加'/'

location  /proxy {
proxy_pass http://192.168.8.1:8090/fa;
}

$ curl -v 'http://192.168.8.90:81/proxy/fa'
*   Trying 192.168.8.90...
* Connected to 192.168.8.90 (192.168.8.90) port 81 (#0)
> GET /proxy/fa HTTP/1.1
> Host: 192.168.8.90:81
> User-Agent: curl/7.45.0
> Accept: */*
>
< HTTP/1.1 404 Not Found
< Server: nginx/1.9.15
< Date: Sat, 20 Apr 2019 08:28:39 GMT
< Content-Type: text/html; charset=utf8
< Content-Length: 169
< Connection: keep-alive
<
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.12.2</center>
</body>
</html>
* Connection #0 to host 192.168.8.90 left intact

curl -v 'http://192.168.8.90:81/proxy/fa/'与上同为404。

配置proxy_pass后,后端服务器的url(request_uri)情况分析

server {
    listen      80;
    server_name www.freeoa.net;

    # 情形A
    # 访问 http://www.freeoa.net/testa/aaaa
    # 后端的request_uri为: /testa/aaaa
    location ^~ /testa/ {
        proxy_pass http://127.0.0.1:8801;
    }
    
    # 情形B
    # 访问 http://www.freeoa.net/testb/bbbb
    # 后端的request_uri为: /bbbb
    location ^~ /testb/ {
        proxy_pass http://127.0.0.1:8801/;
    }

    # 情形C
    # 下面这段location是正确的
    location ~ /testc {
        proxy_pass http://127.0.0.1:8801;
    }

    # 情形D
    # 下面这段location是错误的
    #
    # nginx -t 时,会报如下错误:
    #
    # nginx: [emerg] "proxy_pass" cannot have URI part in location given by regular
    # expression, or inside named location, or inside "if" statement, or inside
    # "limit_except" block in /opt/app/nginx/conf/vhost/test.conf:17
    #
    # 当location为正则表达式时,proxy_pass 不能包含URI部分。本例中包含了"/"
    location ~ /testd {
        proxy_pass http://127.0.0.1:8801/;   # 记住,location为正则表达式时,不能这样写!!!
    }

    # 情形E
    # 访问 http://www.freeoa.net/ccc/bbbb
    # 后端的request_uri为: /aaa/ccc/bbbb
    location /ccc/ {
        proxy_pass http://127.0.0.1:8801/aaa$request_uri;
    }

    # 情形F
    # 访问 http://www.freeoa.net/namea/ddd
    # 后端的request_uri为: /getls?namea=ddd
    location /namea/ {
        rewrite    /namea/([^/]+) /getls?namea=$1 break;
        proxy_pass http://127.0.0.1:8801;
    }

    # 情形G
    # 访问 http://www.freeoa.net/nameb/eee
    # 后端的request_uri为: /getls?nameb=eee
    location /nameb/ {
        rewrite    /nameb/([^/]+) /getls?nameb=$1 break;
        proxy_pass http://127.0.0.1:8801/;
    }

}

本文总结自互联网,感谢网友。