一、nginx配置
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://localhost:8080/;
}
1.$remote_addr
proxy_set_header X-Real-IP $remote_addr;
$remote_addr 的值相当于 web 端使用 request.getRemoteAddr()
2.$proxy_add_x_forwarded_for
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
意思是增加一个 $proxy_add_x_forwarded_for 到 X-Forwarded-For 里去,注意是增加,而不是覆盖,当搭建两台 nginx 在不同的 ip 上,并且都使用了这段配置,那你会发现在 web 服务器端通过 request.getAttribute("X-Forwarded-For") 获得的将会是客户端 ip 和第一台 nginx 的 ip
$proxy_add_x_forwarded_for 变量包含客户端请求头中的 X-Forwarded-For 与 $remote_addr 两部分,他们之间用逗号分开
3.$host
proxy_set_header Host $host;
请求行中的 Host,如果有 Host 请求头,则用其值替换掉请求行中的主机名,如果请求中没有 Host 行和 Host 请求头,则等于请求匹配的 server 名称(处理请求 server 的 server_name 指令的值),值为小写,不包含端口
二、$remote_addr 和 $proxy_add_x_forwarded_for 示例
1.请求链路
有请求链路如下:
client (172.17.0.6) -> nginx_3 (172.17.0.5) -> nginx_2 (172.17.0.4) -> nginx_1 (172.17.0.3) -> server (172.17.0.2)
2.java 代码
@RestController
public class IndexController {
@GetMapping("/")
public String index() {
return "this is index";
}
@GetMapping("/test")
public String test(HttpServletRequest request) {
StringBuilder builder = new StringBuilder();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String value = request.getHeader(name);
builder.append(String.format("%s: %s", name, value));
builder.append("\n");
}
builder.append("request.getRemoteAddr: " + request.getRemoteAddr());
System.out.println(builder);
return builder.toString();
}
}
3.nginx 配置
nginx_1 (172.17.0.3) 配置:
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://172.17.0.2:8080/;
}
nginx2 (172.17.0.4) 配置:
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://172.17.0.3:80/;
}
nginx3 (172.17.0.5) 配置:
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://172.17.0.4:80/;
}
4.执行 (经多层 nginx)
在 client (172.17.0.6) 执行:
root@9f2e34ea4d11:/# curl http://172.17.0.5/test
host: 172.17.0.5
x-real-ip: 172.17.0.4
x-forwarded-for: 172.17.0.6, 172.17.0.5, 172.17.0.4
connection: close
user-agent: curl/7.68.0
accept: */*
request.getRemoteAddr: 172.17.0.3
root@44d615a2f5b2:/# curl -H 'host: abc.com' http://172.17.0.5/test
host: abc.com
x-real-ip: 172.17.0.4
x-forwarded-for: 172.17.0.6, 172.17.0.5, 172.17.0.4
connection: close
user-agent: curl/7.68.0
accept: */*
request.getRemoteAddr: 172.17.0.3
x-forwarded-for 记录每层代理的 $remote_addr 的值
- nginx_3 (172.17.0.5) 获得请求的 $remote_addr (相当于执行 request.getRemoteAddr) 的值为 172.17.0.6,请求头的 x-real-ip 为 172.17.0.6,x-forwarded-for 值为 172.17.0.6;
- nginx_2 (172.17.0.4) 获得请求的 $remote_addr 的值为 172.17.0.5,设置请求头的 x-real-ip 为 172.17.0.5,x-forwarded-for 值为 172.17.0.6, 172.17.0.5;
- nginx_1 (172.17.0.3) 获得请求的 $remote_addr 的值为 172.17.0.4,设置请求头的 x-real-ip 为 172.17.0.4,x-forwarded-for 值为 172.17.0.6, 172.17.0.5, 172.17.0.4;
- web 获取请求头 x-real-ip 为 172.17.0.4,x-forwarded-for 为 172.17.0.6, 172.17.0.5, 172.17.0.4
5.执行 (经一层 nginx)
在 client (172.17.0.6) 执行:
root@9f2e34ea4d11:/# curl http://172.17.0.3/test
host: 172.17.0.3
x-real-ip: 172.17.0.6
x-forwarded-for: 172.17.0.6
connection: close
user-agent: curl/7.68.0
accept: */*
request.getRemoteAddr: 172.17.0.3
6.执行 (不经 nginx)
在 client (172.17.0.6) 执行:
root@9f2e34ea4d11:/# curl http://172.17.0.2:8080/test
host: 172.17.0.2:8080
user-agent: curl/7.68.0
accept: */*
request.getRemoteAddr: 172.17.0.6
综上:
- 如果多层 nginx 代理,x-forwarded-for 的第一个值为客户端 ip,x-real-ip 为最接近服务端口的 ip (注意:分析可知,如果只是最接近客户端的 nginx 配置 x-real-ip,其他 nginx 不配置 x-real-ip,此时 x-real-ip 也是客户端 ip),request.getRemoteAddr 为最接近服务端的 nginx 的 ip
- 如果一层 nginx 代理,x-real-ip、x-forwarded-for 为客户端 ip,request.getRemoteAddr 为 nginx 的 ip
- 如果不经 nginx 代理,request.getRemoteAddr 为客户端 的 ip
三、$host 示例
1.nginx 配置
nginx3 (172.17.0.5) 配置:
# 如果没有显式声明 default_server 则第一个 server 会被隐式的设为 default_server
server {
listen 80;
listen [::]:80;
server_name localhost;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://172.17.0.4:80/;
}
}
server {
listen 80;
listen [::]:80;
server_name aa.com;
location / {
return 200 'This is aa.com';
}
}
server {
listen 80;
listen [::]:80;
server_name bb.com;
location / {
return 200 'This is bb.com';
}
}
# 显示的定义一个 default_server
# server {
# listen 80;
# listen [::]:80;
# server_name default_server;
#
# location / {
# return 200 'This is default_server';
# }
# }
2. client 配置
在 client (172.17.0.6) 配置 /etc/hosts
172.17.0.5 aa.com
172.17.0.5 bb.com
3.执行
在 client (172.17.0.6) 执行:
root@9f2e34ea4d11:/# curl http://aa.com
This is aa.com
root@9f2e34ea4d11:/# curl http://bb.com
This is bb.com
root@9f2e34ea4d11:/# curl -H 'host: bb.com' http://aa.com
This is bb.com
综上:
请求行中的 Host,如果有 Host 请求头,则用其值替换掉请求行中的主机名,如果请求中没有 Host 行和 Host 请求头,则等于请求匹配的 server 名称(处理请求 server 的 server_name 指令的值),值为小写,不包含端口