Nginx 反向代理
什么是代理?
一张图了解两种代理模式的区别:
常见应用场景:
- 正向代理:VPN 翻墙
- 反向代理:Web 站点服务
Nginx 反向代理
Nginx 代理功能由标准 HTTP 模块中内置的 ngx_http_proxy_module 提供,因此无需额外编译,常见配置如下:
1 | http { |
proxy_pass
指令用于配置被代理服务器的协议和地址。除了配置单机,还可以配置集群,详见 Nginx 负载均衡 。
常见问题
To slash or not to slash
Here is a handy table that shows you how the request will be received by your WebApp, depending on how you write the location
and proxy_pass
declarations. Assume all requests go to http://localhost:8080:
location | proxy_pass | Request | Received by upstream |
---|---|---|---|
/webapp/ | http://localhost:8080/api/ | /webapp/foo?bar=baz | /api/foo?bar=baz ✅ |
/webapp/ | http://localhost:8080/api | /webapp/foo?bar=baz | /apifoo?bar=baz |
/webapp | http://localhost:8080/api/ | /webapp/foo?bar=baz | /api//foo?bar=baz |
/webapp | http://localhost:8080/api | /webapp/foo?bar=baz | /api/foo?bar=baz |
/webapp | http://localhost:8080/api | /webappfoo?bar=baz | /apifoo?bar=baz |
In other words: You usually always want a trailing slash, never want to mix with and without trailing slash, and only want without trailing slash when you want to concatenate a certain path component together (which I guess is quite rarely the case).
上游无法获取真实的访问来源信息
使用反向代理之后,上游服务器(如 Tomcat)无法获取真实的访问来源信息(如协议、域名、访问 IP),例如下面代码:
1 | request.getScheme() // 总是 http,而不是实际的 http 或 https |
这个问题需要在 Nginx 和 Tomcat 中做一些配置以解决问题。
Nginx 配置:
使用 proxy_set_header
指令为上游服务器添加请求头:
1 | http { |
Tomcat 配置:
在 Tomcat 的 server.xml
中配置 RemoteIpValve 让代码能够获取真实 IP 和协议:
1 | <Valve className="org.apache.catalina.valves.RemoteIpValve" |
解决结果:
1 | request.getScheme() // 实际的 http 或 https |
全局 proxy_set_header 失效
先来看下 proxy_set_header
的语法:
1 | 语法: proxy_set_header field value; |
这里隐含一个坑:如果当前配置级别中定义了 proxy_set_header
指令,哪怕只配置了一个,都会导致无法从上面的级别继承配置,即导致全局级别的 proxy_set_header
配置失效。例如下述 HTTP 长连接配置:
1 | http { |
导致全局级别的 proxy_set_header
配置失效:
解决办法是在 location
中重新配置这四个请求头。