- 博客/
nginx请求头X-Real-IP与X-Forwarded-For
前言#
nginx反向代理后端应用服务器时,遇到web应用服务器需要获取客户端真实IP,该如何正确配置nginx?本文分别在一级反向代理及两级反向代理情况下,测试请求头X-Real-IP与X-Forwarded-For如何配置
一级代理测试验证#
1.测试架构: nginx反向代理 + tomcat web服务
ip | server | server port |
---|---|---|
192.168.196.135 | nginx | 31005 |
192.168.196.136 | tomcat | 31001 |
192.168.196.1 | 客户端 | - |
2.部署tomcat测试war包
$cat test.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%
out.println("Protocol: " + request.getProtocol());
out.println("Server Name: " + request.getServerName());
out.println("Server Port: " + request.getServerPort());
out.println("Remote Addr: " + request.getRemoteAddr());
out.println("Remote Host: " + request.getRemoteHost());
out.println("Request URL: " + request.getRequestURL());
out.println("Host: " + request.getHeader("Host"));
out.println("X-Real-IP: " + request.getHeader("X-Real-IP"));
out.println("X-Forwarded-For: " + request.getHeader("X-Forwarded-For"));
out.println("Connection : " + request.getHeader("Connection"));
%>
$jar cvf th.war th/test.jsp
3.修改nginx.conf
upstream backendsrv {
server 192.168.196.136:31005;
}
server {
listen 31001;
server_name www.test.com;
location /th {
root html;
index index.html index.htm;
proxy_pass http://backendsrv;
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;
}
}
nginx重设请求头X-Real-IP为$remote_addr、 X-Forwarded-For为$proxy_add_x_forwarded_for
$proxy_add_x_forwarded_for:
$remote_addr变量值添加在客户端“X-Forwarded-For”请求头的后面,并以逗号分隔。 如果客户端请求未携带“X-Forwarded-For”请求头,$proxy_add_x_forwarded_for 变量值将与$remote_addr变量相同
4.使用curl构造X-Real-IP、X-Forwarded-For请求头,在客户端发起请求
$curl -H "X-Forwarded-For: 1.1.1.1" -H "X-Real-IP: 2.2.2.2" 192.168.196.135:31001/th/test.jsp
Protocol: HTTP/1.0
Server Name: 192.168.196.135
Server Port: 31001
Remote Addr: 192.168.196.135
Remote Host: 192.168.196.135
Request URL: http://192.168.196.135:31001/th/test.jsp
Host: 192.168.196.135:31001
X-Real-IP: 192.168.196.1
X-Forwarded-For: 1.1.1.1, 192.168.196.1
Connection : close
根据请求结果看出:
- nginx清理了伪造的X-Real-IP请求头并重写为clientIP-
- nginx在X-Forwarded-For后追加clientIP
后端web应用可通过X-Real-IP
或者X-Forwarded-For的最后一个IP
获取clientIP
5.本例中,nginx为一级代理,为避免客户端伪造X-Forwarded-For请求头,可添加 proxy_set_header X-Forwarded-For $remote_addr
upstream backendsrv {
server 192.168.196.136:31005;
}
server {
listen 31001;
server_name www.test.com;
location /th {
root html;
index index.html index.htm;
proxy_pass http://backendsrv;
proxy_set_header Host $http_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;
}
}
$curl -H "X-Forwarded-For: 1.1.1.1" 192.168.196.135:31001/th/test.jsp
Protocol: HTTP/1.0
Server Name: 192.168.196.135
Server Port: 31001
Remote Addr: 192.168.196.135
Remote Host: 192.168.196.135
Request URL: http://192.168.196.135:31001/th/test.jsp
Host: 192.168.196.135:31001
X-Real-IP: 192.168.196.1
X-Forwarded-For: 192.168.196.1
Connection : close
二级代理测试验证#
1.测试架构: nginx1反向代理 +nginx2反向代理+ tomcat web服务
ip | server | server port |
---|---|---|
192.168.196.133 | nginx1 | 31009 |
192.168.196.135 | nginx2 | 31001 |
192.168.196.136 | tomcat | 31005 |
192.168.196.1 | client | - |
2.修改nginx.conf
一级反向代理nginx1
upstream backendnginx {
server 192.168.196.135:31001;
}
server {
listen 31009;
server_name 192.168.196.133;
location /th {
root html;
index index.html index.htm;
proxy_pass http://backendnginx;
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;
}
}
二级反向代理nginx2
upstream backendsrv {
server 192.168.196.136:31005;
}
server {
listen 31001;
server_name www.test.com;
location /th/ft {
root html;
index index.html index.htm;
proxy_pass http://backendsrv;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
3.使用curl构造X-Forwarded-For请求头,在客户端发起请求
$curl -H "X-Forwarded-For: unknown, 1.1.1.1" 192.168.196.133:31009/th/ft/test.jsp
Protocol: HTTP/1.0
Server Name: 192.168.196.133
Server Port: 31009
Remote Addr: 192.168.196.135
Remote Host: 192.168.196.135
Request URL: http://192.168.196.133:31009/th/ft/test.jsp
Host: 192.168.196.133:31009
X-Real-IP: 192.168.196.1
X-Forwarded-For: unknown, 1.1.1.1, 192.168.196.1, 192.168.196.133
Connection : close
nginx依次将$remote_addr追加到X-Forwarded-For
clientIP为倒数第n个IP, n为proxy的次数
4.面向客户端的第一级nginx中,添加proxy_set_header X-Forwarded-For $remote_addr;
upstream backendnginx {
server 192.168.196.135:31001;
}
server {
listen 31009;
server_name 192.168.196.133;
location /th {
root html;
index index.html index.htm;
proxy_pass http://backendnginx;
proxy_set_header Host $http_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;
}
}
$curl -H "X-Forwarded-For: unknown, 1.1.1.1" 192.168.196.133:31009/th/ft/test.jsp
Protocol: HTTP/1.0
Server Name: 192.168.196.133
Server Port: 31009
Remote Addr: 192.168.196.135
Remote Host: 192.168.196.135
Request URL: http://192.168.196.133:31009/th/ft/test.jsp
Host: 192.168.196.133:31009
X-Real-IP: 192.168.196.1
X-Forwarded-For: 192.168.196.1, unknown, 1.1.1.1, 192.168.196.1, 192.168.196.133
Connection : close
X-Forwarded-For的第一个IP为clientIP
最佳实践#
第一级反向代理nginx: 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;
中间的反向代理nginx只需配置: proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;