nginx实战之反向代理
1、反向代理
所谓反向代理就是nginx接受到请求后,将请求转发给内部的一台服务器处理,比如我们启动了一个tomcat是8081端口,但是我们防火墙不会开放8081端口,只会开放nginx的80端口,那么我们需要再nginx上进行反向代理配置,让到80端口的请求转发给8081端口处理,这样安全性也有很大的提高,并且后续可以进行负载均衡:80端口进来,从代理的多台服务器按策略选一台处理。
1.1、8081端口服务搭建
我们这里就不启动tomcat来模拟服务了,直接在nginx上配置个8081端口的server即可,nginx配置文件做如下处理
server {
listen 8081;
server_name localhost;
location / {
root html;
index index8081.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
这里的意思是,请求从8081进来,会返回index8081.html的页面,这个页面会在nginx目录下面的html里面,内容如下
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to 8081!</h1>
</body>
</html>
到这里就相当于有一个新的服务了。
1.2、nginx反向代理配置
下面我们来配置负载均衡,我们在nginx.conf的http配置里面新增upstream.
upstream first {
server localhost:8081;
}
然后,在80的server进行代理配置
server {
listen 80;
server_name localhost;
location / {
#反向代理配置
proxy_pass http://first;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
1.3、重新启动nginx
我们可以先用-t命令校验下配置文件是否正确
./nginx -t
检查正确后,进行重新加载
./nginx -s reload
1.4、访问测试
我们访问80端口,然后可以看到返回了index8081.html的页面内容,表面反向代理成功。

1.5、proxy_set_header
我们看到,反向代理设置的时候,有进行proxy_set_header设置,那这个的作用是什么呢?
即允许重新定义或添加字段传递给代理服务器的请求头,简而言之,proxy_set_header 就是可设置请求头-并将头信息传递到服务器端,不属于请求头的参数中也需要传递时,重定义下即可!
主要有三个参数需要设置
Host
若客户端发过来的请求header中有HOST这个字段,$http_host和$host表示的就是原始请求
X-Real-IP
是指客户端的真实IP,如果设置了$remote_addr这个值,后端服务器就能获取到客户端的真实IP
X-Forwarded-For
这个变量的值有$proxy_add_x_forwarded_for,可以真实的显示出客户端原始ip。
我们这里来测试下
环境搭建
在本机搭建一个springboot应用,springboot应用搭建方法参考:从官网开始用IDEA在JDK1.8环境搭建springboot2.7.14项目,然后代码如下
@RestController
public class DemoController {
@GetMapping("/hello")
public Map<String,Object> hello(HttpServletRequest request){
Map<String,Object> result = new TreeMap<String,Object>();
result.put("hello","Hello World");
String scheme = request.getScheme();
String xForwardedFor = request.getHeader("X-Forwarded-For");
String xRealIp = request.getHeader("X-Real-IP");
String host = request.getHeader("Host");
String serverName = request.getServerName();
int serverPort = request.getServerPort();
String remoteName = request.getRemoteAddr();
int remotePort = request.getRemotePort();
result.put("scheme",scheme);
result.put("serverName",serverName);
result.put("serverPort",serverPort);
result.put("remoteName",remoteName);
result.put("remotePort",remotePort);
result.put("xForwardedFor",xForwardedFor);
result.put("xRealIp",xRealIp);
result.put("host",host);
return result;
}
}
我本机的ip为:192.168.192.1
nginx所在的ip:192.168.192.11
nginx的反向代理的配置为
upstream first {
server 192.168.192.1:8080;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://first;
#proxy_set_header Host $host;
#proxy_set_header X-Real-IP $remote_addr;
#proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
这样客户端通过访问nginx暴露的80服务就是反向代理到我本机启动的springboot应用。下面我们来测试proxy_set_header的作用
不配置proxy_set_header的测试
我们上面是把proxy_set_header都注释了,此时在浏览器上访问
http://192.168.192.11/hello
返回了
{"hello":"Hello World","host":"first","remoteName":"192.168.192.11","remotePort":35058,"scheme":"http","serverName":"first","serverPort":80,"xForwardedFor":null,"xRealIp":null}
可以看到完全没有拿到任何客户端的信息.
配置proxy_set_header的测试
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://first;
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://192.168.192.11/hello
返回了
{"hello":"Hello World","host":"192.168.192.11","remoteName":"192.168.192.11","remotePort":35060,"scheme":"http","serverName":"192.168.192.11","serverPort":80,"xForwardedFor":"192.168.192.1","xRealIp":"192.168.192.1"}
request.getRemoteAddr():获取的永远都是代理对象的IP
X-Forwarded-For和X-Real-IP:都获取了客户端的真实IP
X-Forwarded-For和X-Real-IP有什么区别呢
我们上面的例子发现,这两个获取的都是客户端的真实IP地址,其实如果只有一个反向代理服务器,那结果都是一样的,但是如果有多个,这里可以就用一个nginx来模拟一下。配置如下
我们首先在nginx配置一个服务,代理到我们的springboot应用.
upstream second {
server 192.168.192.1:8080;
}
server {
listen 8081;
server_name localhost;
location / {
proxy_pass http://second;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
这里相当于我们的应用被一个nginx服务器代理了,端口是8081,我们再配置一个80端口代理到8081,就模拟了两台代理服务器的情况
upstream first {
server localhost:8081;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://first;
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://192.168.192.11/hello,返回了
{"hello":"Hello World","host":"192.168.192.11","remoteName":"192.168.192.11","remotePort":35086,"scheme":"http","serverName":"192.168.192.11","serverPort":80,"xForwardedFor":"192.168.192.1, 127.0.0.1","xRealIp":"127.0.0.1"}
可以看到X-Real-IP获取的就不是客户端的地址了,而是第一次代理发起的地址,但是我们的X-Forwarded-For却有全部的客户端IP地址,我们想要获取真实客户端,只需要获取X-Forwarded-For第一个即可。
总结:X-Forwarded-For记录服务器的地址,会把记录添加到结尾,X-Real-IP用来记录服务器的地址,它不把记录添加到结尾,而是直接替换。所以用X-Forwarded-For就不会错。