反向代理与 Real-IP 和 X-Forwarded-For

2023-10-28

开篇语:开涛新作《亿级流量网站架构核心技术》出版计划公布以来,博文视点遭受到一波又一波读者询问面世时间的DDos攻击。面对亿级流量的热情,感激之余,我们也很庆幸——这部作品质量的确过硬,不会辜负拥趸厚望,更有开涛的高度负责和体贴周到加持,让她绝对物超所值、长久流芳。下面,看一篇来自这位技术暖男的售前支持。
——本书策划编辑 侠少

  本文作者张开涛。为保障《亿级流量网站架构核心技术》一书内容的连续性,有些需要读者了解的内容,或者书的补充和引申内容,会通过二维码嵌入的方式引导读者阅读学习。大家可以关注作者公众号“开涛的博客”,并从菜单栏“我的新书”中查阅相关内容。

  本文是「4.4 接入层限流」节中的「按照IP限制并发连接数配置示例」部分需要了解的内容。
  当我们访问互联网上的服务时,大多数时,客户端并不是直接访问到服务端的,而是客户端首先请求到反向代理,反向代理再转发到服务端实现服务访问,通过反向代理实现路由/负载均衡等策略。这样一来在服务端拿到的客户端IP将是反向代理IP,而不是真实客户端IP,因此需要一种办法来获取到真实客户端IP。
  如下图所示,客户端通过Nginx Proxy1 和 Nginx Proxy2 两层反向代理才访问到具体服务Nginx Backend(或如Tomcat服务)。那Nginx Backend如何才能拿到真实客户端IP呢?
图片描述

  接下来我们来看看如何才能获取到客户端真实IP。

场景1

  场景1是很简单的场景,Nginx Proxy直接把请求往后转发,没有做任何处理。

Nginx Proxy
192.168.107.107 nginx.conf
location /test {
    proxy_pass http://192.168.107.112:8080;
}
192.168.107.112 nginx.conf
location /test {
    proxy_pass http://192.168.107.114:8080;
}
Nginx Proxy就是简单的把请求往后转发。
Nginx Backend
192.168.107.114 nginx.conf
location /test {
    default_type text/html;
    charset gbk;
    echo "$remote_addr || $http_x_forwarded_for";
}

  Nginx Backend输出客户端IP($remote_addr)和X-Forwarded-For请求头($http_x_forwarded_for),当访问服务时输出结果如下所示:

192.168.107.112 ||

分析

1.$remote_addr代表客户端IP,当前配置的输出结果为最后一个代理服务器的IP,并不是真实客户端IP;
2.在没有特殊配置情况下,X-Forwarded-For请求头不会自动添加到请求头中,即Nginx Backend的$http_x_forwarded_for输出为空。

场景2

  场景2通过添加X-Real-IP和X-Forwarded-For捕获客户端真实IP。

Nginx Proxy
192.168.107.107 nginx.conf
location /test {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://192.168.107.112:8080;
}
192.168.107.112 nginx.conf
location /test {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://192.168.107.114:8080;
}
Nginx Backend
192.168.107.114 nginx.conf
location /test {
    default_type text/html;
    charset gbk;
    echo "$remote_addr ||$http_x_real_ip  ||$http_x_forwarded_for";
}

  当访问服务时,输出结果为:

192.168.107.112 || 192.168.162.16 || 192.168.162.16, 192.168.107.107

分析

1.在离用户最近的反向代理NginxProxy 1,通过“proxy_set_header X-Real-IP $remote_addr”把真实客户端IP写入到请求头X-Real-IP,在NginxBackend输出$http_x_real_ip获取到了真实客户端IP;而Nginx Backend的“$remote_addr”输出为最后一个反向代理的IP;
2.“proxy_set_headerX-Forwarded-For $proxy_add_x_forwarded_for”的是把请求头中的X-Forwarded-For与$remote_addr用逗号合起来,如果请求头中没有X-Forwarded-For则$proxy_add_x_forwarded_for为$remote_addr。
  X-Forwarded-For代表了客户端IP,反向代理如Nginx通过$proxy_add_x_forwarded_for添加此项,X-Forwarded-For的格式为X-Forwarded-For:real client ip, proxy ip 1, proxy ip N,每经过一个反向代理就在请求头X-Forwarded-For后追加反向代理IP。
  到此我们可以使用请求头X-Real-IP和X-Forwarded-For来获取客户端IP及客户端到服务端经过的反向代理IP了。这种方式还是很麻烦,$remote_addr并不是真实客户端IP。

场景3

  为了更方便的获取真实客户端IP,可以使用nginx http_realip_module模块解决,在安装nginx时通过–with-http_realip_module安装该模块。NginxProxy配置和场景2一样。

Nginx Backend
192.168.107.114 nginx.conf
real_ip_header X-Forwarded-For; 
set_real_ip_from 192.168.0.0/16; 
real_ip_recursive on; 

location /test {
    default_type text/html;
    charset gbk;
    echo "$remote_addr || $http_x_real_ip  ||$http_x_forwarded_for";
}

  当访问服务时,输出结果为:

192.168.162.16 || 192.168.162.16 || 192.168.162.16, 192.168.107.107

分析

1.X-Real-IP和X-Forwarded-For和场景2一样;
2.使用realip模块后,$remote_addr输出结果为真实客户端IP,可以使用$realip_remote_addr获取最后一个反向代理的IP;
3.real_ip_headerX-Forwarded-For:告知Nginx真实客户端IP从哪个请求头获取,如X-Forwarded-For;
4.set_real_ip_from192.168.0.0/16:告知Nginx哪些是反向代理IP,即排除后剩下的就是真实客户端IP,支持配置具体IP地址、CIDR地址;
5.real_ip_recursive on:是否递归解析,当real_ip_recursive配置为off时,Nginx会把real_ip_header指定的请求头中的最后一个IP作为真实客户端IP;当real_ip_recursive配置为on时,Nginx会递归解析real_ip_header指定的请求头,最后一个不匹配set_real_ip_from的IP作为真实客户端IP。
  如果配置“real_ip_recursive off; ”,则输出结果为:

192.168.107.107 || 192.168.162.16 ||192.168.162.16, 192.168.107.107

总结

1.通过“proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for” 把从真实客户端IP和反向代理IP通过逗号分隔,添加到请求头中;
2.可以在第一个反向代理上配置“proxy_set_header X-Real-IP $remote_addr” 获取真实客户端IP;
3.配合realip模块从X-Forwarded-For也可以获取到真实客户端IP。
  在整个反向代理链条的第一个反向代理可以不配置“proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for”,否则客户端可以伪造X-Forwarded-For从而伪造客户端真实IP,如果服务端使用X-Forwarded-For第一个IP作为真实客户端IP,则就出问题了。如果通过配置X-Real-IP请求头或者配合realip模块也不会出现该问题。如果自己解析X-Forwarded-For的话,记得使用realip算法解析,而不是取第一个。
当我们进行限流时一定注意限制的是真实客户端IP,而不是反向代理IP,我遇到过很多同事在这块出问题的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

反向代理与 Real-IP 和 X-Forwarded-For 的相关文章

  • 关于springmvc的面试经验分享

    1 什么是 Spring MVC Spring MVC 是一个基于 Java 的实现了 MVC 设计模式的请求驱动类型的轻量级 Web 框架 通过把 Model View Controller 分离 将 web 层进行职责解耦 把复杂的 w
  • K8s 架构简介(一)

    一 前言 在开始学习K8s之前 让我们对容器有一个基本的了解 1 1 什么是容器 一个容器镜像是一个可运行的软件包 其中包含了一个完整的可执行程序 包括代码和运行时需要应用 系统库和全部重要设置的默认值 通过将应用程序本身 和其依赖容器化
  • 2022年数字化转型的三大基于云的驱动因素

    未来一年将标志着企业品牌 工作和生活创新的最大重置 文章来源 Venture Beat Google Cloud CTO Will Grannis 数字技术一直是并将持续是公司应对新冠疫情的背后推动力 从购物和供应链到儿童保育和工作 一切都
  • 12_Linux ARM架构_安装JDK8-银河麒麟V10(Kylin Linux Advanced Server V10 )操作系统

    12 Linux ARM架构 安装JDK8 银河麒麟V10 Kylin Linux Advanced Server V10 操作系统 1 官网下载aarch64架构jdk包 2 linux服务器中创建java文件夹 方便后期快速寻找 3 将
  • 一文带你从IntelliJ IDEA中一键生成Controller、Service、Dao、Model层代码,真的不看看吗?

    前言 EasyCode插件介绍与安装 简介EasyCode是基于IntelliJ IDEA开发的代码生成插件 支持自定义任意模板 Java html js xml 只要是与数据库相关的代码都可以通过自定义模板来生成 支持数据库类型与java
  • 【实践篇】领域驱动设计:DDD工程参考架构

    背景 为什么要制定参考工程架构 不同团队落地DDD所采取的应用架构风格可能不同 并没有统一的 标准的DDD工程架构 有些团队可能遵循经典的DDD四层架构 或改进的DDD四层架构 有些团队可能综合考虑分层架构 整洁架构 六边形架构等多种架构风
  • HCIE云计算之FusionCloud 6.3部署架构

    HCIE云计算之FusionCloud 6 3部署架构 一 不同的type类型场景需求 二 Region Type 1部署方案 1 Region Type 1简介 2 Region Type 1部署私有云介绍 3 Region Type 1
  • [架构之路-185]-《软考-系统分析师》-3-操作系统基本原理 - 文件索引表

    目录 一 文件的索引块 二 索引分配表 三 索引表的链接方案 四 多层索引 五 混合索引分配 一 文件的索引块 存放在目录中的文件 并非是文件的真实内容 目录中记录了文件的索引块是几号磁盘块 文件对应的索引表是存放在指定的磁盘块中的 二 索
  • FastJSON、Jackson、Gson性能测试

    起因是公司原先用的是阿里开源的FastJSON 大家用的也比较顺手 但是在出现了两次严重的漏洞后 公司决定放弃FastJSON 使用其他序列化 反序列化工具 考虑大家常用的无非就是FastJSON Jackson和Gson这三种 因此领导让
  • 项目实战之RabbitMQ冗余双写架构

    作者名称 DaenCode gt https blog csdn net 2302 79094329 作者简介 啥技术都喜欢捣鼓捣鼓 喜欢分享技术 经验 生活 人生感悟 尝尽人生百味 方知世间冷暖 所属专栏 项目所感所想 gt https
  • 深入微服务架构 | 微服务与k8s架构解读

    微服务项目架构解读 什么是微服务 微服务是指开发一个单个小型的但有业务功能的服务 每个服务都有自己的处理和轻量通讯机制 可以部署在单个或多个服务器上 微服务也指一种种松耦合的 有一定的有界上下文的面向服务架构 也就是说 如果每个服务都要同时
  • 单个 epoll + 线程池与每个线程一个 epoll 这两种架构哪个更适合大量短连接的场景?

    本文是回答一位知友的提问 单个 epoll 线程池与每个线程一个 epoll 这两种架构哪个更适合大量短连接的场景 不少教程上都提到线程池适合大量的网络短连接的任务场景 但我总感觉这个优势有点站不住脚 单 epoll 线程池模型 主要考虑到
  • GoLong的学习之路,进阶,微服务之序列化协议,Protocol Buffers V3

    这章是接上一章 使用 RPC包 序列化中没有详细去讲 因为这一块需要看的和学习的地方很多 并且这一块是RPC中可以说是最重要的一块 也是性能的重要影响因子 今天这篇主要会讲其使用方式 文章目录 Protocol Buffers V3 背景以
  • 人工智能与底层架构:构建智能引擎的技术支柱

    导言 人工智能与底层架构的交融塑造了智能系统的基石 是推动智能时代发展的关键动力 本文将深入研究人工智能在底层架构中的关键作用 以及它对智能引擎的技术支持 探讨人工智能在计算机底层架构中的作用 以及这一融合如何塑造数字化未来 1 人工智能与
  • Autosar诊断——配置部分服务功能寻址不响应,抑制肯定响应

    Autosar诊断 简介和功能概述 Autosar诊断 DCM模块内的子模块 Autosar诊断 DSL Diagnostic Session Layer Autosar诊断 DSD Diagnostic Service Dispatche
  • 什么是微服务

    微服务是一种架构风格 它把一个大型的复杂软件应用划分为一系列小的服务 每个服务都具有单一的功能 运行在其自己的进程中 并通常基于不同的编程语言和框架 这些服务之间通过轻量级通信机制相互通信 这种通信机制基于HTTP协议 微服务架构风格使得系
  • 微服务常见的配置中心简介

    微服务架构中 常见的配置中心包括以下几种 Spring Cloud Config Spring Cloud Config是官方推荐的配置中心解决方案 它支持将配置文件存储在Git SVN等版本控制系统中 通过提供RESTful API 各个
  • 微服务常见的配置中心简介

    微服务架构中 常见的配置中心包括以下几种 Spring Cloud Config Spring Cloud Config是官方推荐的配置中心解决方案 它支持将配置文件存储在Git SVN等版本控制系统中 通过提供RESTful API 各个
  • 在nginx中设置“X-Forwarded-For”标头的目的是什么

    我的 Django 应用程序有以下 Nginx 配置 upstream api server localhost 8000 server listen 80 location proxy pass http api proxy redire
  • 专车数据层架构进化往事:好的架构是进化来的,不是设计来的

    很多年前 读了 子柳 老师的 淘宝技术这十年 这本书成为了我的架构启蒙书 书中的一句话像种子一样深埋在我的脑海里 好的架构是进化来的 不是设计来的 2015 年 我加入神州专车订单研发团队 亲历了专车数据层 架构进化 的过程 这次工作经历对

随机推荐