客户端浏览器:192.168.110.1
代理服务器1:192.168.110.130
代理服务器2:192.168.110.128
web服务器1:192.168.110.129
web服务器2:192.168.110.132
1.1web服务器配置文件相同
#cat /opt/nginx1.14.2/conf/nginx.conf
user root;
worker_processes auto;
pid /opt/nginx1.14.2/nginx.pid;
daemon on;
error_log logs/error_v1.log;
events {
worker_connections 65535;
}
worker_rlimit_nofile 65535;
http {
include mime.types;
default_type application/octet-stream;
client_max_body_size 300m;
fastcgi_intercept_errors on;
log_format main 'remote_addr:"$remote_addr"'
' - http_host:"$http_host"'
' - request:"$request"'
' - "clientRealIp:"$clientRealIp"'
' - http_X-Real-IP:"$http_x_real_ip"'
' - http_x_forwarded_for:"$http_x_forwarded_for"';
access_log logs/access_v1.log main;
map $http_x_forwarded_for $clientRealIp {
"" $remote_addr;
~^(?P<firstAddr>[0-9\\.]+),?.*$ $firstAddr;
}
server {
listen 80;
server_name www.a.com;
location / {
root html;
index index.html;
}
}
}
1.2 web服务器测试主页
web服务器1
#cat /opt/nginx1.14.2/html/bigdata/index.html
Welcome to nginx_129!
web服务器2
#cat /opt/nginx1.14.2/html/bigdata/index.html
Welcome to nginx_132!
1.3 直接访问web服务器输出日志
web服务器1
remote_addr:"192.168.110.1" - http_host:"192.168.110.129" - request:"GET /bigdata/ HTTP/1.1" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"-" - http_x_forwarded_for:"-"
web服务器2
remote_addr:"192.168.110.1" - http_host:"192.168.110.132" - request:"GET /bigdata/ HTTP/1.1" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"-" - http_x_forwarded_for:"-"
结论:客户端未通过代理直接访问网站 remote_addr均为客户端IP地址 http_host为服务器IP地址 客户端真实地址clientRealIp即为remote_addr的地址
2.1 代理服务器2配置
[root@elk-master-node ~]# cat /opt/nginx1.14.2/conf/nginx.conf
user root;
worker_processes auto;
pid /opt/nginx1.14.2/nginx.pid;
daemon on;
error_log logs/error_v1.log;
events {
worker_connections 65535;
}
worker_rlimit_nofile 65535;
http {
include mime.types;
default_type application/octet-stream;
client_max_body_size 300m;
fastcgi_intercept_errors on;
log_format main 'remote_addr:"$remote_addr"'
' - http_host:"$http_host"'
' - request:"$request"'
' - "clientRealIp:"$clientRealIp"'
' - http_X-Real-IP:"$http_x_real_ip"'
' - http_x_forwarded_for:"$http_x_forwarded_for"';
access_log logs/access_v1.log main;
map $http_x_forwarded_for $clientRealIp {
"" $remote_addr;
~^(?P<firstAddr>[0-9\\.]+),?.*$ $firstAddr;
}
upstream tpbigdata {
server 192.168.110.132;
server 192.168.110.129;
}
server {
listen 8080 ;
server_name www.a.com;
proxy_set_header Host $host;
#proxy_set_header Host $proxy_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#proxy_set_header X-Forwarded-For $remote_addr;
location /bigdata {
proxy_pass http://tpbigdata;
#proxy_set_header Host $host;
#proxy_set_header X-Real-IP $remote_addr;
#proxy_set_header X-Forwarded-For $remote_addr;
#proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
error_page 500 502 503 504 400 403 /50x.html;
location = /50x.html {
root html;
}
}
}
2.2 通过代理服务器2访问网站
访问方式 http://192.168.110.128:8080/bigdata/
代理服务器2日志
remote_addr:"192.168.110.1" - http_host:"192.168.110.128:8080" - request:"GET /bigdata/ HTTP/1.1" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"-" - http_x_forwarded_for:"-"
web服务器日志
remote_addr:"192.168.110.128" - http_host:"192.168.110.128" - request:"GET /bigdata/ HTTP/1.0" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"192.168.110.1" - http_x_forwarded_for:"192.168.110.1"
客户端添加hosts
192.168.110.128 www.a.com
访问方式 http://www.a.com:8080/bigdata/
代理服务器2日志
remote_addr:"192.168.110.1" - http_host:"www.a.com:8080" - request:"GET /bigdata/ HTTP/1.1" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"-" - http_x_forwarded_for:"-"
web服务器日志
remote_addr:"192.168.110.128" - http_host:"www.a.com" - request:"GET /bigdata/ HTTP/1.0" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"192.168.110.1" - http_x_forwarded_for:"192.168.110.1"
2.3 修改代理服务器2配置
注释proxy_set_header Host $host;
放开proxy_set_header Host $proxy_host;
2.4通过代理服务器2访问网站
访问方式 http://192.168.110.128:8080/bigdata/
代理服务器2日志
remote_addr:"192.168.110.1" - http_host:"192.168.110.128:8080" - request:"GET /favicon.ico HTTP/1.1" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"-" - http_x_forwarded_for:"-"
web服务器日志
remote_addr:"192.168.110.128" - http_host:"tpbigdata" - request:"GET /bigdata/ HTTP/1.0" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"192.168.110.1" - http_x_forwarded_for:"192.168.110.1"
访问方式 http://www.a.com:8080/bigdata/
代理服务器2日志
remote_addr:"192.168.110.1" - http_host:"www.a.com:8080" - request:"GET /bigdata/ HTTP/1.1" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"-" - http_x_forwarded_for:"-"
web服务器日志
remote_addr:"192.168.110.128" - http_host:"tpbigdata" - request:"GET /bigdata/ HTTP/1.0" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"192.168.110.1" - http_x_forwarded_for:"192.168.110.1"
结论:客户端通过代理直接访问网站 remote_addr均为代理服务器2IP地址 当开启$host时http_host为客户端访问ip地址 当开启$proxy时http_host为proxy_pass http://tpbigdata;中http://域名或者IP
客户端真实地址clientRealIp即为http_x_forwarded_for的第一个地址 X-real-ip是一个自定义的变量名,名字可以随意取值为$remote_addr时用户的真实ip就被放在X-real-ip这个变量里了。http_x_forwarded_for为服务器传过来的$X-Forwarded-For值
2.5 修改代理服务器2配置
注释proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
放开proxy_set_header X-Forwarded-For $remote_addr;
2.6通过代理服务器2访问网站
访问方式 http://192.168.110.128:8080/bigdata/
代理服务器2日志
remote_addr:"192.168.110.1" - http_host:"192.168.110.128:8080" - request:"GET /bigdata/ HTTP/1.1" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"-" - http_x_forwarded_for:"-"
web服务器日志
remote_addr:"192.168.110.128" - http_host:"tpbigdata" - request:"GET /bigdata/ HTTP/1.0" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"192.168.110.1" - http_x_forwarded_for:"192.168.110.1"
访问方式 http://www.a.com:8080/bigdata/
代理服务器2日志
remote_addr:"192.168.110.1" - http_host:"www.a.com:8080" - request:"GET /bigdata/ HTTP/1.1" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"-" - http_x_forwarded_for:"-"
web服务器日志
remote_addr:"192.168.110.128" - http_host:"tpbigdata" - request:"GET /bigdata/ HTTP/1.0" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"192.168.110.1" - http_x_forwarded_for:"192.168.110.1"
结论:客户端通过代理直接访问网站 remote_addr均为代理服务器2IP地址 当开启$host时http_host为客户端访问ip地址 当开启$proxy时http_host为proxy_pass http://tpbigdata;中http://域名或者IP
客户端真实地址clientRealIp即为http_x_forwarded_for的第一个地址 X-real-ip是一个自定义的变量名,名字可以随意取值为$remote_addr时用户的真实ip就被放在X-real-ip这个变量里了。http_x_forwarded_for为服务器传过来的$remote_add值。
proxy_set_header X-Forwarded-For $remote_addr;和proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;两者的区别:在只有一个代理服务器的转发的情况下,两者的效果貌似差不多,都可以真实的显示出客户端原始ip,但是区别在于:$proxy_add_x_forwarded_for变量包含客户端请求头中的"X-Forwarded-For",与$remote_addr两部分,他们之间用逗号分开。
举个例子,有一个web应用,在它之前通过了两个nginx转发,www.linuxidc.com 即用户访问该web通过两台nginx。
在第一台nginx中,使用
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
现在的$proxy_add_x_forwarded_for变量的"X-Forwarded-For"部分是空的,所以只有$remote_addr,而$remote_addr的值是用户的ip,于是赋值以后,X-Forwarded-For变量的值就是用户的真实的ip地址了。
到了第二台nginx,使用
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
现在的$proxy_add_x_forwarded_for变量,X-Forwarded-For部分包含的是用户的真实ip,$remote_addr部分的值是上一台nginx的ip地址,于是通过这个赋值以后现在的X-Forwarded-For的值就变成了
“用户的真实ip,第一台nginx的ip”。
2.7 代理服务服务器1保持默认配置
[root@elk-data-node2 ~]# cat /opt/nginx1.14.2/conf/nginx.conf
user root;
worker_processes auto;
pid /opt/nginx1.14.2/nginx.pid;
daemon on;
error_log logs/error_v1.log;
events {
worker_connections 65535;
}
worker_rlimit_nofile 65535;
http {
include mime.types;
default_type application/octet-stream;
client_max_body_size 300m;
fastcgi_intercept_errors on;
log_format main 'remote_addr:"$remote_addr"'
' - http_host:"$http_host"'
' - request:"$request"'
' - "clientRealIp:"$clientRealIp"'
' - http_X-Real-IP:"$http_x_real_ip"'
' - http_x_forwarded_for:"$http_x_forwarded_for"';
access_log logs/access_v1.log main;
map $http_x_forwarded_for $clientRealIp {
"" $remote_addr;
~^(?P<firstAddr>[0-9\\.]+),?.*$ $firstAddr;
}
upstream 130tpbigdata {
server 192.168.110.128:8080;
}
server {
listen 443 ;
server_name www.a.com;
ssl on ;
server_tokens off;
ssl_certificate cert.crt;
ssl_certificate_key cert.key;
location /bigdata {
proxy_pass http://130tpbigdata;
}
error_page 500 502 503 504 400 403 /50x.html;
location = /50x.html {
root html;
}
}
2.8通过代理服务器1访问网站
访问方式 https://192.168.110.130/bigdata/
代理服务器1日志
remote_addr:"192.168.110.1" - http_host:"192.168.110.130" - request:"GET /bigdata/ HTTP/1.1" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"-" - http_x_forwarded_for:"-"
代理服务器2日志
remote_addr:"192.168.110.130" - http_host:"tpbigdata" - request:"GET /bigdata/ HTTP/1.0" - "clientRealIp:"192.168.110.130" - http_X-Real-IP:"-" - http_x_forwarded_for:"-"
web服务器日志
remote_addr:"192.168.110.128" - http_host:"tpbigdata" - request:"GET /bigdata/ HTTP/1.0" - "clientRealIp:"192.168.110.130" - http_X-Real-IP:"192.168.110.130" - http_x_forwarded_for:"192.168.1
10.130"
客户端添加hosts
192.168.110.130 www.a.com
访问方式 https://www.a.com/bigdata/
代理服务器1日志
remote_addr:"192.168.110.1" - http_host:"www.a.com" - request:"GET /bigdata/ HTTP/1.1" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"-" - http_x_forwarded_for:"-"
代理服务器2日志
remote_addr:"192.168.110.130" - http_host:"tpbigdata" - request:"GET /bigdata/ HTTP/1.0" - "clientRealIp:"192.168.110.130" - http_X-Real-IP:"-" - http_x_forwarded_for:"-"
web服务器日志
remote_addr:"192.168.110.128" - http_host:"tpbigdata" - request:"GET /bigdata/ HTTP/1.0" - "clientRealIp:"192.168.110.130" - http_X-Real-IP:"192.168.110.130" - http_x_forwarded_for:"192.168.1
10.130"
此时修改代理服务器2的配置
放开 proxy_set_header Host $host;
注释 proxy_set_header Host $proxy_host;
放开proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
注释proxy_set_header X-Forwarded-For $remote_addr;
添加代理服务器1配置
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
访问方式 https://192.168.110.130/bigdata/
代理服务器1日志
remote_addr:"192.168.110.1" - http_host:"192.168.110.130" - request:"GET /bigdata/ HTTP/1.1" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"-" - http_x_forwarded_for:"-"
代理服务器2日志
remote_addr:"192.168.110.130" - http_host:"130tpbigdata" - request:"GET /bigdata/ HTTP/1.0" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"192.168.110.1" - http_x_forwarded_for:"192.168.11
0.1"
web服务器日志
remote_addr:"192.168.110.128" - http_host:"130tpbigdata" - request:"GET /bigdata/ HTTP/1.0" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"192.168.110.130" - http_x_forwarded_for:"192.168.
110.1, 192.168.110.130"
访问方式 https://www.a.com/bigdata/
代理服务器1日志
remote_addr:"192.168.110.1" - http_host:"www.a.com" - request:"GET /bigdata/ HTTP/1.1" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"-" - http_x_forwarded_for:"-"
代理服务器2日志
remote_addr:"192.168.110.130" - http_host:"130tpbigdata" - request:"GET /bigdata/ HTTP/1.0" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"192.168.110.1" - http_x_forwarded_for:"192.168.11
0.1"
web服务器日志
remote_addr:"192.168.110.128" - http_host:"130tpbigdata" - request:"GET /bigdata/ HTTP/1.0" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"192.168.110.130" - http_x_forwarded_for:"192.168.
110.1, 192.168.110.130"
修改代理服务2配置
在location中添加 proxy_set_header Host $proxy_host;
访问方式 https://192.168.110.130/bigdata/
代理服务器1日志
remote_addr:"192.168.110.1" - http_host:"192.168.110.130" - request:"GET /bigdata/ HTTP/1.1" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"-" - http_x_forwarded_for:"-"
代理服务器2日志
remote_addr:"192.168.110.130" - http_host:"130tpbigdata" - request:"GET /bigdata/ HTTP/1.0" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"192.168.110.1" - http_x_forwarded_for:"192.168.11
0.1"
web服务器日志
remote_addr:"192.168.110.128" - http_host:"tpbigdata" - request:"GET /bigdata/ HTTP/1.0" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"192.168.110.1" - http_x_forwarded_for:"192.168.110.1
"
访问方式 https://www.a.com/bigdata/
代理服务器1日志
remote_addr:"192.168.110.1" - http_host:"www.a.com" - request:"GET /bigdata/ HTTP/1.1" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"-" - http_x_forwarded_for:"-"
代理服务器2日志
remote_addr:"192.168.110.130" - http_host:"130tpbigdata" - request:"GET /bigdata/ HTTP/1.0" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"192.168.110.1" - http_x_forwarded_for:"192.168.11
0.1"
web服务器日志
remote_addr:"192.168.110.128" - http_host:"tpbigdata" - request:"GET /bigdata/ HTTP/1.0" - "clientRealIp:"192.168.110.1" - http_X-Real-IP:"192.168.110.1" - http_x_forwarded_for:"192.168.110.1
"
结论
1.Host
proxy_set_header Host $host;
获取客户端访问的头部,它的值在请求包含"Host"请求头时为"Host"字段的值,在请求未携带"Host"请求头时为虚拟主机的主域名。
proxy_set_header Host $proxy_host
默认host的值为proxy_pass后面跟的那个域名或者IP
例如 nginx upstream 名称为tpbigdata 客户端访问域名为www.a.com 设置proxy_set_header Host $host;则后端接收的http_host为www.a.com 设置为proxy_set_header Host $proxy_host则后端接收的http_host为tpbigdata
2.X-Forwarded-For
proxy_set_header X-Forwarded-For $remote_addr;和proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;两者的区别:在只有一个代理服务器的转发的情况下,两者的效果差不多,都可以真实的显示出客户端原始ip,但是区别在于:$proxy_add_x_forwarded_for变量包含客户端请求头中的"X-Forwarded-For",与$remote_addr两部分,他们之间用逗号分开。
例如 有一个web应用,在它之前通过了两个nginx转发,www.a.com 即用户访问该web通过两台nginx。
在第一台nginx中,使用proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
现在的$proxy_add_x_forwarded_for变量的"X-Forwarded-For"部分是空的,所以只有$remote_addr,而$remote_addr的值是用户的ip,于是赋值以后,X-Forwarded-For变量的值就是用户的真实的ip地址了。
到了第二台nginx,使用proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
现在的$proxy_add_x_forwarded_for变量,X-Forwarded-For部分包含的是用户的真实ip,$remote_addr部分的值是上一台nginx的ip地址,于是通过这个赋值以后现在的X-Forwarded-For的值就变成了“用户的真实ip,第一台nginx的ip”。
3. X-Real-IP
X-Real-IP只是一个变量,后面的设置会覆盖前面的设置(跟X-Forwarded-For的追加特性区别明显),所以我们一般只在第一个代理设置proxy_set_header X-Real-IP $remote_addr;就好了,然后再应用端直接引用$http_x_real_ip就行.
例如 客户端请求web经过三个代理服务器(代理服务器都设置此参数为$remot_addr) nginx1将请求转发给nginx2 nginx2请求转发给nginx3 nginx3转发请求给web 则web服务器接收$http_x_real_ip值为nginx3地址,nginx3服务器接收$http_x_real_ip值为nginx2地址,nginx2服务器接收$http_x_real_ip值为nginx1地址,nginx1服务器接收$http_x_real_ip值为客户端地址。
4.在当前级别的配置中没有定义 proxy_set_header 指令时,这些指令从上级继承。如果当前级别的配置中已经定义了 proxy_set_header 指令,在上级中定义的 proxy_set_header 指令在当前级别都会失效.
5.keepalive_timeout 600;
#客户端链接超时时间。为0的时候禁用长连接。
6.keepalive_requests 10000;
#在一个长连接上可以服务的最大请求数目。
#当达到最大请求数目且所有已有请求结束后,连接被关闭。
#默认值为100
7.proxy_http_version 1.1;
# 设置http版本为1.1
8.proxy_connect_timeout
nginx 与 upstream server 发起握手等候响应超时时间,默认为 60s;
9.proxy_read_timeout
nginx 接收 upstream server 数据超时, 默认 60s, 如果连续的 60s 内没有收到 1 个字节, 连接关闭
10.proxy_send_timeout
nginx 发送数据至 upstream server 超时, 默认 60s, 如果连续的 60s 内没有发送 1 个字节, 连接关闭
11.client_max_body_size
默认 1M,表示 客户端请求服务器最大允许大小,在“Content-Length”请求头中指定。如果请求的正文数据大于client_max_body_size,HTTP协议会报错 413 Request Entity Too Large。就是说如果请求的正文大于client_max_body_size,一定是失败的。如果需要上传大文件,一定要修改该值。
12.proxy_redirect
修改从被代理服务器传来的应答头中的"Location"和"Refresh"字段
13.proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
NGINX 通过在客户端和后端服务器之间建立起一条隧道来支持WebSocket。为了使NGINX可以将来自客户端的Upgrade请求发送给后端服务器,Upgrade和Connection的头信息必须被显式的设置
#################################################解析###########################################################
map $http_x_forwarded_for $clientRealIp {
"" $remote_addr;
~^(?P<firstAddr>[0-9\\.]+),?.*$ $firstAddr;
}
1.通过 map 指令,我们为 nginx 创建了一个变量 $clientRealIp ,这个就是 原始用户的真实 IP 地址,不论用户是直接访问,还是通过一串 CDN 之后的访问,我们都能取得正确的原始IP地址。
当一个 CDN 或者透明代理服务器把用户的请求转到后面服务器的时候,这个 CDN 服务器会在 Http 的头中加入 一个记录
X-Forwarded-For : 用户IP, 代理服务器IP
如果中间经历了不止一个 代理服务器,中间建立多层代理之后,这个 记录会是这样
X-Forwarded-For : 用户IP, 代理服务器1-IP, 代理服务器2-IP, 代理服务器3-IP, ….
可以看到经过好多层代理之后, 用户的真实IP 在第一个位置, 后面会跟一串 中间代理服务器的IP地址,从这里取到用户真实的IP地址,针对这个 IP 地址做限制就可以了。
2.## 没有通过代理也就是X-Forwarded-For没有值,直接用 remote_addr
default ""; ---->default : 指定如果没有匹配结果将使用的默认值。当没有设置 default,将会用一个空的字符串作为默认的结果。
#####################################################################################################################
remote_addr代表客户端的IP,但它的值不是由客户端提供的,而是服务端根据客户端的ip指定的,当你的浏览器访问某个网站时,假设中间没有任何代理,那么网站的web服务器(Nginx,Apache等)就会把remote_addr设为你的机器IP,如果你用了某个代理,那么你的浏览器会先访问这个代理,然后再由这个代理转发到网站,这样web服务器就会把remote_addr设为这台代理机器的IP。
########################################参考############################################################################
nginx proxy_set_header设置,自定义header
在实际应用中,我们可能需要获取用户的ip地址,比如做异地登陆的判断,或者统计ip访问次数等,通常情况下我们使用request.getRemoteAddr()就可以获取到客户端ip,但是当我们使用了nginx作为反向代理后,使用request.getRemoteAddr()获取到的就一直是nginx服务器的ip的地址,那这时应该怎么办?
而且有些场景做了一些客户端浏览器url的判断,比如,浏览器输入baidu.com是可以访问到百度的,但是输入!@#¥*.com有可能也是可以访问到百度,但是百度内部并不希望以这种方式访问(或者防止一些网络攻击),这时候应该怎么办?
其实nginx允许重新定义或者添加发往后端服务器的请求头。value可以包含文本、变量或者它们的组合。
默认情况下,有两个请求头会被重新定义:
proxy_set_header Host $proxy_host; //默认会将后端服务器的HOST填写进去
proxy_set_header Connection close;
我们可以通过设置nginx配置去调整转发报文的头部:
proxy_set_header X-real-ip $remote_addr;
其中这个X-real-ip是一个自定义的变量名,名字可以随意取,这样做完之后,用户的真实ip就被放在X-real-ip这个变量里了,然后,在web端可以这样获取:
request.getHeader("X-real-ip")
proxy_set_header X-Forwarded-For $remote_addr;
同上。
真实的显示出客户端原始ip。(nginx更多使用这条配置,X-Forwarded-For为默认字段,以下介绍均为默认字段)
proxy_set_header Host $http_host;
如果想获取客户端访问的头部,可以这样来设置。
但是,如果客户端请求头中没有携带这个头部,那么传递到后端服务器的请求也不含这个头部。
proxy_set_header Host $host;
这个配置相当于上面配置的增强。
它的值在请求包含"Host"请求头时为"Host"字段的值,在请求未携带"Host"请求头时为虚拟主机的主域名。
proxy_set_header Host $host:$proxy_port;
服务器名和后端服务器的端口(访问端口)一起传送。
proxy_set_header <<<*>>> "";
请求头的值为空,请求头将不会传送给后端服务器。
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
在默认情况下经过proxy转发的请求,在后端看来远程地址都是proxy端的ip 。
添加这条配置之后:
意思是增加一个$proxy_add_x_forwarded_for到X-Forwarded-For里去,注意是增加,而不是覆盖,当然由于默认的X-Forwarded-For值是空的,所以我们总感觉X-Forwarded-For的值就等于$proxy_add_x_forwarded_for的值,实际上当你搭建两台nginx在不同的ip上,并且都使用了这段配置,那你会发现在web服务器端通过request.getHeader("X-Forwarded-For")获得的将会是客户端ip和第一台nginx的ip。
在第一台nginx中,使用
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
现在的$proxy_add_x_forwarded_for变量的"X-Forwarded-For"部分是空的,所以只有$remote_addr,而$remote_addr的值是用户的ip,于是赋值以后,X-Forwarded-For变量的值就是用户的真实的ip地址了。
到了第二台nginx,也使用
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
现在的$proxy_add_x_forwarded_for变量,
X-Forwarded-For部分包含的是用户的真实ip,
$remote_addr部分的值是上一台nginx的ip地址,
于是通过这个赋值以后现在的X-Forwarded-For的值就变成了"用户的真实ip,第一台nginx的ip"。
##########################参考#####################################################################
nginx 反向代理之 proxy_set_header
proxy_set_header用来设定被代理服务器接收到的header信息。
语法:proxy_set_header field value;
field :为要更改的项目,也可以理解为变量的名字,比如host
value :为变量的值
如果不设置proxy_set_header,则默认host的值为proxy_pass后面跟的那个域名或者IP(一般写IP)
proxy_set_header X-Real-IP $remote_addr;
和
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
用来设置被代理端接收到的远程客户端IP,如果不设置,则header信息中并不会透传远程真实客户端的IP地址。
配置示例:
server {
listen 80;
server_name www.xxx.com;
location /aming/
{
proxy_pass http://192.168.1.10:8080/linux/;
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 proxy_set_header 配置注意事项
如果没有特别注意 proxy_set_header 配置,使用 proxy_set_header 可能会引起以下问题:
丢失需要的 header 信息
拿到意外的 Host 信息
upstream 中的 keepalive 不能生效
官方文档
Allows redefining or appending fields to the request header passed to the proxied server. The value can contain text, variables, and their combinations. These directives are inherited from the previous level if and only if there are no proxy_set_header directives defined on the current level. By default, only two fields are redefined:
允许重新定义或附加字段到传递给代理服务器的请求头。该值可以包含文本、变量及其组合。当且仅当当前级别上没有定义 proxy_set_header 指令时,这些指令从上级继承。默认情况下,只有两个值被重新定义:
proxy_set_header Host $proxy_host;
proxy_set_header Connection close;
问题的关键
在当前级别的配置中没有定义 proxy_set_header 指令时,这些指令从上级继承。
如果当前级别的配置中已经定义了 proxy_set_header 指令,在上级中定义的 proxy_set_header 指令在当前级别都会失效
举个例子:
这个配置,如果用户访问 example.com/test/index.html,后端服务拿到的 Host 值是 example.com_test,而不是期望的 example.com;后端服务器会收到 Connection: close 的 Header,而不能复用连接;后端服务器也不能从 Header 中获取到 X-Real-IP。
http {
...
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Connection "";
proxy_set_header X-Real-IP $remote_addr;
upstream example.com_test {
server 127.0.0.1:8080;
keepalive 16;
}
server {
server_name example.com;
location ^~ /test/ {
proxy_set_header test test;
proxy_pass http://example.com_test;
}
}
}
注意: 在 location ^~ /test/ {...} 中真正生效的 proxy_set_header 只有这三个
proxy_set_header Host $proxy_host;
proxy_set_header Connection close;
proxy_set_header test test;
正确的配置
http {
...
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Connection "";
proxy_set_header X-Real-IP $remote_addr;
upstream example.com_test {
server 127.0.0.1:8080;
keepalive 16;
}
server {
server_name example.com;
location ^~ /test/ {
proxy_set_header test test;
proxy_set_header Host $host;
proxy_set_header Connection "";
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://example.com_test;
}
}
}
参考:http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header
#########################################参考#######################################
HTTP 请求头中的 X-Forwarded-For
X-Forwarded-For和相关几个头部的理解
$remote_addr
是nginx与客户端进行TCP连接过程中,获得的客户端真实地址. Remote Address 无法伪造,因为建立 TCP 连接需要三次握手,如果伪造了源 IP,无法建立 TCP 连接,更不会有后面的 HTTP 请求
X-Real-IP
是一个自定义头。X-Real-Ip 通常被 HTTP 代理用来表示与它产生 TCP 连接的设备 IP,这个设备可能是其他代理,也可能是真正的请求端。需要注意的是,X-Real-Ip 目前并不属于任何标准,代理和 Web 应用之间可以约定用任何自定义头来传递这个信息
X-Forwarded-For
X-Forwarded-For 是一个扩展头。HTTP/1.1(RFC 2616)协议并没有对它的定义,它最开始是由 Squid 这个缓存代理软件引入,用来表示 HTTP 请求端真实 IP,现在已经成为事实上的标准,被各大 HTTP 代理、负载均衡等转发服务广泛使用,并被写入 RFC 7239(Forwarded HTTP Extension)标准之中.
X-Forwarded-For请求头格式非常简单,就这样:
X-Forwarded-For:client, proxy1, proxy2
可以看到,XFF 的内容由「英文逗号 + 空格」隔开的多个部分组成,最开始的是离服务端最远的设备 IP,然后是每一级代理设备的 IP。
如果一个 HTTP 请求到达服务器之前,经过了三个代理 Proxy1、Proxy2、Proxy3,IP 分别为 IP1、IP2、IP3,用户真实 IP 为 IP0,那么按照 XFF 标准,服务端最终会收到以下信息:
X-Forwarded-For: IP0, IP1, IP2
Proxy3 直连服务器,它会给 XFF 追加 IP2,表示它是在帮 Proxy2 转发请求。列表中并没有 IP3,IP3 可以在服务端通过 remote_address 字段获得。我们知道 HTTP 连接基于 TCP 连接,HTTP 协议中没有 IP 的概念,remote_address 来自 TCP 连接,表示与服务端建立 TCP 连接的设备 IP,在这个例子里就是 IP3。
详细分析一下,这样的结果是经过这样的流程而形成的:
用户IP0---> 代理Proxy1(IP1),Proxy1记录用户IP0,并将请求转发个Proxy2时,带上一个Http Header
X-Forwarded-For: IP0
Proxy2收到请求后读取到请求有 X-Forwarded-For: IP0,然后proxy2 继续把链接上来的proxy1 ip追加到 X-Forwarded-For 上面,构造出X-Forwarded-For: IP0, IP1,继续转发请求给Proxy 3
同理,Proxy3 按照第二部构造出 X-Forwarded-For: IP0, IP1, IP2,转发给真正的服务器,比如NGINX,nginx收到了http请求,里面就是 X-Forwarded-For: IP0, IP1, IP2 这样的结果。所以Proxy 3 的IP3,不会出现在这里。
nginx 获取proxy3的IP 能通过remote_address获取到,因为这个remote_address就是真正建立TCP链接的IP,这个不能伪造,是直接产生链接的IP。$remote_address 无法伪造,因为建立 TCP 连接需要三次握手,如果伪造了源 IP,无法建立 TCP 连接,更不会有后面的 HTTP 请求。
x-forwarded-for 实践研究:
uwsgi_pass的情况下,nginx 没有设置proxy_pass x-forwarded-for: $proxy_add_x_forwarded_for;
如果请求头传了XFF,在flask里面能正常读取请求头里面的XFF,就是当是一个普通的头读出;如果header不传这个XFF的话,就读不到
proxy_pass 情况下
没有传 # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for 的话,跟上面的uwsgi_pass 一样,都是在没有设置header XFF情况下,读不到。
如果传了 proxy_set_header X-Forwarded-For proxy_add_x_forwarded_for, header 不传xff 的话,也是可以在程序里面读到Xff 头: X-Forwarded-For: 10.0.2.2 (这个IP就是真正连上nginx 的IP, 也就是remote_address),因为这句proxy_set_header 会让nginx追加一个$remote_address到XFF。
header 传xff的话, 程序里面可以读到Xff 头: X-Forwarded-For: 188.103.19.120, 10.0.2.2 (第一个是我自己编的,第二个是remote_address),nginx还是会因为proxy_set_header X-Forwarded-Forproxy_add_x_forwarded_for 这句而追加$remote_addr到XFF。
总结:
只要nginx前端(例如lvs, varnish)转发请求给nginx的时候,带了x-forwarded-for ,那么程序就一定能读到这个字段,如果nginx还设置了proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for, 那么程序能读到XFF是:ip0, ip1 (客户端Ip,lvs或者varnishIP)。 如果nginx没有设置,那么nginx还是会原样把http头传给程序,也就是说程序也能读到XFF,而且XFF就是ip0 客户端IP。
proxy_pass 设置这个头 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 是站在一个作为代理的角度把。能继续传输多级代理的头。
nginx的日志格式写了$http_x_forwared_for 说明前端(lvs)确实传了这个头过来。所以是程序是读取到的
uwsgi_pass 不能设置 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 这个头,是因为这个头是对http代理来说,用来传递IP的,uwsgi 不可能充当一个代理。
nginx->程序,这里其实有两个链接过程,其他IP与nginx的TCP链接, nginx与程序的TCP链接。所以$remote_addr都是对各自来说的。
程序的remote_addr: remote_addr 127.0.0.1 (跟它链接的是nginx 内网127.0.0.1)
nginx的remote_addr : X-Real-Ip: 10.0.2.2 (跟它链接的是我的电脑,IP 10.0.2.2)
对程序来说,读取的request.remote_addr 也永远是直接跟他链接的ip, 也就是反向代理nginx
The access_route attribute uses the X-Forwarded-For header, falling back to the REMOTE_ADDRWSGI variable; 也就是说access_route默认读取XFF头,如果没有,降级读取WSGI的REMOTE_ADDR变量,这个 WSGI的REMOTE_ADDR变量 就是 $remote_addr
request.envron 是WSGI的变量,都是wsgi server转过来的,普通的头都是加了HTTP_前缀的 ,包括proxy_set_header Host host:8000; proxy_set_header X-Forwarded-Forproxy_add_x_forwarded_for;
添加的头都会出现在处理,因为他们就是普通的http头
LVS->nginx的情况下, 请求的时候主动加XFF,程序读取的时候没显示。因为LVS设置XFF的时候,直接把直连的IP赋值给LVS,忽略掉所有本来有的XFF,要从LVS这里开始。 所以程序读到的XFF是 :XFF headers 218.107.55.254, 10.120.214.252
前面的是我的IP, 后面的是LVS的IP
{
"wsgi.multiprocess": "False",
"SERVER_SOFTWARE": "Werkzeug/0.11.10",
"SCRIPT_NAME": "",
"REQUEST_METHOD": "GET",
"PATH_INFO": "/api/get_agreement_url/",
"SERVER_PROTOCOL": "HTTP/1.0",
"QUERY_STRING": "",
"werkzeug.server.shutdown": "<function shutdown_server at 0x7f4a2f4e5488>",
"CONTENT_LENGTH": "",
"SERVER_NAME": "127.0.0.1",
"REMOTE_PORT": 58284,
"werkzeug.request": "",
"wsgi.url_scheme": "http",
"SERVER_PORT": "6000",
"HTTP_POSTMAN_TOKEN": "666cfd97-585b-c342-f0bd-5c785dfff27d",
"wsgi.input": "",
"wsgi.multithread": "False",
"HTTP_CACHE_CONTROL": "no-cache",
"HTTP_ACCEPT": "*/*",
"wsgi.version": "(1, 0)",
"wsgi.run_once": "False",
"wsgi.errors": "",
"CONTENT_TYPE": "",
"REMOTE_ADDR": "127.0.0.1",
"HTTP_CONNECTION": "close",
"HTTP_USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36",
"HTTP_ACCEPT_LANGUAGE": "zh-CN,zh;q=0.8,en;q=0.6",
"HTTP_X_FORWARDED_FOR": "10.0.2.2",
"HTTP_ACCEPT_ENCODING": "gzip, deflate, sdch",
"HTTP_HOST": "[test.mumu.nie.netease.com:8000](http://test.mumu.nie.netease.com:8000/)",
}
proxy_add_x_forwarded_for; nginx的这个变量含义就是,每次都追加remote_address 到 xff头,如果xff头不存在,那么xff就被设置成跟$remote_address 一样了。如果本来就存在,就追加了 ip1, ip2这样的形式
################################################参考########################################################################
X-Real-IP
log_format main '$http_x_forwarded_for|$http_x_real_ip|$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
下面我们看一下有多级代理存在时如何获取客户端真实IP.
首先要明确在header里面的 X-Real-IP只是一个变量,后面的设置会覆盖前面的设置(跟X-Forwarded-For的追加特性区别明显),所以我们一般只在第一个代理设置proxy_set_header X-Real-IP $remote_addr;就好了,然后再应用端直接引用$http_x_real_ip就行.
1.假如我们只在proxy01设置了 X-Real-IP
192.168.247.1, 192.168.247.131, 192.168.247.132|192.168.247.1|101.254.182.6 - - [23/May/2017:11:23:00 +0800] "GET /test/ HTTP/1.0" 200 9 "http://test.proxy.com/test/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36" "192.168.247.1, 192.168.247.131, 192.168.247.132"
2.假如我们只在proxy02设置了X-Real-IP
192.168.247.1, 192.168.247.131, 192.168.247.132|192.168.247.131|101.254.182.6 - - [23/May/2017:11:26:22 +0800] "GET /test/ HTTP/1.0" 200 9 "http://test.proxy.com/test/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36" "192.168.247.1, 192.168.247.131, 192.168.247.132"
3.假如我们只在proxy03设置了X-Real-IP
192.168.247.1, 192.168.247.131, 192.168.247.132|192.168.247.132|101.254.182.6 - - [23/May/2017:11:27:21 +0800] "GET /test/ HTTP/1.0" 200 9 "http://test.proxy.com/test/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36" "192.168.247.1, 192.168.247.131, 192.168.247.132"
4.所有代理都设置X-Real-IP
192.168.247.1, 192.168.247.131, 192.168.247.132|192.168.247.132|101.254.182.6 - - [23/May/2017:11:29:09 +0800] "GET /test/ HTTP/1.0" 200 9 "http://test.proxy.com/test/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36" "192.168.247.1, 192.168.247.131, 192.168.247.132"
5.强迫症来了,再试一个只设置proxy01,proxy02的看看
192.168.247.1, 192.168.247.131, 192.168.247.132|192.168.247.131|101.254.182.6 - - [23/May/2017:11:30:36 +0800] "GET /test/ HTTP/1.0" 200 9 "http://test.proxy.com/test/" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36" "192.168.247.1, 192.168.247.131, 192.168.247.132"
假如有人假冒X-Real-IP呢?
6. 在proxy01上执行: curl localhost/admin -H 'X-Forwarded-For: 1.1.1.1' -H 'X-Real-IP: xx.xx.xx.xx'
1.1.1.1, 127.0.0.1, 192.168.247.131, 192.168.247.132|192.168.247.131|101.254.182.6 - - [23/May/2017:11:36:02 +0800] "GET /admin HTTP/1.0" 301 263 "-" "curl/7.15.5 (i386-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5" "1.1.1.1, 127.0.0.1, 192.168.247.131, 192.168.247.132"
并没有影响.
#####################################################参考#####################################
Nginx之proxy_redirect使用详解
更新时间:2018年12月14日 15:03:14 作者:Sun-shell 我要评论
这篇文章主要介绍了Nginx之proxy_redirect使用详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
今天在做nginx反向代理apache的时候出了一点点问题,原来后端apache用的端口是8080通过反向代理后,使用wireshark抓包发现location头域数值为http://192.168.1.154:8080/wuman/ 如果把这个返回给客户端肯定是不可以的,看起来别扭而且还暴露了apache的具体信息
所以在这里用到了nginx的proxy_redirect指定修改被代理服务器返回的响应头中的location头域跟refresh头域数值
以下是截取nginx的一小段配置文档
server {
listen 80;
server_name www.boke.com;
location / {
proxy_pass http://192.168.1.154:8080;
proxy_redirect off;
}
}
此时我们通过curl查看结果得出
[root@localhost nginx]# curl -I http://www.boke.com/wuman
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Thu, 24 Dec 2015 12:02:00 GMT
Content-Type: text/html; charset=iso-8859-1
Connection: keep-alive
Location: http://192.168.1.154:8080/wuman/
这里location为带有后端服务器实际地址跟端口的响应头信息这样在实际线上是不允许的所以这里我们打算通过proxy_redirect将被代理服务器的响应头中的location字段进行修改后返回给客户端
server {
listen 80;
server_name www.boke.com;
location / {
proxy_pass http://192.168.1.154:8080;
proxy_redirect http://192.168.1.154:8080/wuman/ http://www.boke.com/wuman/;
}
server {
listen 80;
server_name www.boke.com;
location / {
proxy_pass http://192.168.1.154:8080;
proxy_redirect ~^http://192.168.1.154:8080(.*) http://www.boke.com$1;
}
则curl查看返回结果
[root@localhost nginx]# curl -I http://www.boke.com/wuman
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Thu, 24 Dec 2015 12:08:34 GMT
Content-Type: text/html; charset=iso-8859-1
Connection: keep-alive
Location: http://www.boke.com/wuman/
此时查看location已经变成了我们想要的结果了。 此时通过replacement 301重定向到了我们新的页面
出处:
proxy_redirect
语法:proxy_redirect [ default|off|redirect replacement ]
默认值:proxy_redirect default
使用字段:http, server, location
如果需要修改从被代理服务器传来的应答头中的"Location"和"Refresh"字段,可以用这个指令设置。
假设被代理服务器返回Location字段为: http://localhost:8000/two/some/uri/
这个指令:
proxy_redirect http://localhost:8000/two/ http://frontend/one/;
将Location字段重写为http://frontend/one/some/uri/。
在代替的字段中可以不写服务器名:
proxy_redirect http://localhost:8000/two/ /;
这样就使用服务器的基本名称和端口,即使它来自非80端口。
如果使用“default”参数,将根据location和proxy_pass参数的设置来决定。
例如下列两个配置等效:
location / one / {
proxy_pass http: //upstream:port/two/;
proxy_redirect default;
}
location / one / {
proxy_pass http: //upstream:port/two/;
proxy_redirect http: //upstream:port/two/ /one/;
}
在指令中可以使用一些变量:
proxy_redirect http://localhost:8000/ http://$host:$server_port/;
这个指令有时可以重复:
proxy_redirect default;
proxy_redirect http://localhost:8000//; proxy_redirect ; /;
参数off将在这个字段中禁止所有的proxy_redirect指令:
proxy_redirect off;
proxy_redirect default;
proxy_redirect http://localhost:8000/ /; proxy_redirect ; /;
利用这个指令可以为被代理服务器发出的相对重定向增加主机名:
如果觉得我的文章对您有用,请点赞。您的支持将鼓励我继续创作!
赞1
添加新评论0 条评论