1.反向代理
Nginx 的反向代理实际上是客户端和真实的应用服务器之间的一个桥梁,客户端(一般是浏览器)访问 Nginx 服务器, Nginx 再去访问 Web 应用服务器。对于 Web 应用来说,这次 HTTP 请求的客户端是 Nginx 而非真实的客户端浏览器,如果不做特殊处理的话,Web 应用会把 Nginx 当作请求的客户端,获取到的客户端信息就是 Nginx 的一些信息。
所以说,后端程序拿到的请求头其实是 nginx 发出的,而不是原来 web 端发出的。
注:我们在浏览器控制台看到的是 web 端的请求头,并不是最终 nginx 代理转发后的请求头
对于改变请求头,下面拿绕过跨域举个例子
先设置好 fe.sherlocked93.club 和 be.sherlocked93.club 二级域名,都指向本云服务器地址,虽然对应 IP 是一样的,但是在 fe.sherlocked93.club 域名发出的请求访问 be.sherlocked93.club 域名的请求还是跨域了,因为访问的 host 不一致。
在前端服务地址为 fe.sherlocked93.club 的页面请求 be.sherlocked93.club 的后端服务导致的跨域,可以这样配置:
server {
listen 9001;
server_name fe.sherlocked93.club;
location / {
proxy_pass be.sherlocked93.club;
}
}
这样就将对前一个域名 fe.sherlocked93.club 的请求全都代理到了be.sherlocked93.club,前端的请求都被我们用服务器代理到了后端地址下,绕过了跨域。
2.静态代理
1)root:拼接
location /image {
root /root/webapp/img;
}
http://xysmxh.com/image/1.png ==> /root/webapp/img/image/1.png(拼接 /image)
2)alias:别名(推荐)
location /image {
alias /root/webapp/img;
}
http://xysmxh.com/image/1.png ==> /root/webapp/img/1.png(替换 /image)
- alias = root + rewrite 去掉 /image
- 使用 alias 时,目录后面一定要加“/",root 可加可不加
更详细的内容可以参考 https://www.jianshu.com/p/4be0d5882ec5
3.记开发时遇到的问题
下面记录一个再开发中遇到的一个问题,需求是统计当天访问服务器的 IP 数量。照理说在应用程序中拿到用户端的 IP 就是 request.getRemoteAddr() 就行了
- request.getRequestURL() = http://localhost:8080/userrequest
- getReqeustURI() = /userreqeust
- getRemoteAddr() = 10.4.64.22
但是,如果经过 nginx 代理了,Servlet应用通过 request.getRemoteAddr() 取到的 IP是 Nginx 的IP地址,并非客户端真实IP,通过 request.getRequestURL() 获取的域名、协议、端口都是 Nginx 访问Web应用时的域名、协议、端口,而非客户端浏览器地址栏上的真实域名、协议、端口。
问题复盘(参考)
例如在某一台IP为 43.107.136.120 的服务器上,Tomcat 端口号为 8080,Nginx 端口号80,Nginx 反向代理 8080 端口:
server {
listen 80;
location / {
proxy_pass http://127.0.0.1:8080; # 本机
}
}
在另一台机器上用浏览器打开 http://43.107.136.120/test 访问某个Servlet应用,获取客户端IP和URL:
System.out.println("RemoteAddr: " + request.getRemoteAddr());
System.out.println("URL: " + request.getRequestURL().toString());
结果是:
RemoteAddr: 127.0.0.1
URL: http://127.0.0.1:8080/test
可以发现,Servlet 程序获取到的客户端 IP 是 Nginx 的 IP 而非浏览器所在机器的 IP,获取到的 URL 是 Nginx 的 proxy_pass 配置的URL组成的地址,而非浏览器地址栏上的真实地址。
解决方案
Nginx 对请求头做以下配置
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- Host:保存客户端真实的域名和端口号
- X-Real-IP:保存客户端真实的IP
- X-Forwarded-For:这个 Header 和 X-Real-IP 类似,但它在多层代理时会包含真实客户端及中间每个代理服务器的 IP
PS:关于 XFF 头可以参考这篇文章
然后在代码中,我们通过 XFF 头去获取真实 IP
public class IpAddrUtils {
public static String getIpAddr(HttpServletRequest request){
// 从请求头 nginx 的 X-Forwarded-For 获取 IP
String ipAddress = request.getHeader("X-Forwarded-For");
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if(ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")){
// 根据网卡取本机配置的IP
InetAddress inet=null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ipAddress= inet.getHostAddress();
}
}
// 对于通过多个代理的情况,会有多个ip值,第一个IP为客户端真实IP,多个IP按照','分割
// "***.***.***.***".length() = 15
if(ipAddress != null && ipAddress.length() > 15){
if(ipAddress.indexOf(",")>0){
ipAddress = ipAddress.substring(0,ipAddress.indexOf(","));
}
}
return ipAddress;
}
}
4.项目配置示例
server {
listen 80;
server_name www.xysmxh.com xysmxh.com; # 注:可以同时配置多个 server_name,空格隔开(默认有 localhost)
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
location /api {
rewrite ^.+api/?(.*)$ /$1 break; # 重写,转发时去掉 /api
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real_Ip $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /image {
alias /root/webapp/img;
}
}






还没有评论,来说两句吧...